From 9c64b2c62614749cd17b9ebfc608650803c9139d Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Tue, 9 Mar 2021 23:00:01 +0100 Subject: [PATCH 01/16] Editor integration Added two functions for importing animations. They work much like importspritesheet --- .../gurkenlabs/utiliti/components/Editor.java | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java index d95dfdb15..fb1da5aec 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java +++ b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java @@ -1,5 +1,4 @@ package de.gurkenlabs.utiliti.components; - import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; @@ -30,6 +29,16 @@ import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.bind.JAXBException; +import de.gurkenlabs.litiengine.Game; +import de.gurkenlabs.litiengine.environment.tilemap.IImageLayer; +import de.gurkenlabs.litiengine.environment.tilemap.ITileset; +import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterData; +import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterLoader; +import de.gurkenlabs.litiengine.gui.screens.Screen; +import de.gurkenlabs.litiengine.resources.ImageFormat; +import de.gurkenlabs.litiengine.resources.ResourceBundle; +import de.gurkenlabs.litiengine.resources.Resources; +import de.gurkenlabs.litiengine.resources.SoundFormat; import de.gurkenlabs.litiengine.Game; import de.gurkenlabs.litiengine.environment.tilemap.IImageLayer; import de.gurkenlabs.litiengine.environment.tilemap.ITileset; @@ -41,7 +50,6 @@ import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterData; import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterLoader; import de.gurkenlabs.litiengine.gui.screens.Screen; -import de.gurkenlabs.litiengine.resources.ImageFormat; import de.gurkenlabs.litiengine.resources.ResourceBundle; import de.gurkenlabs.litiengine.resources.Resources; import de.gurkenlabs.litiengine.resources.SoundFormat; @@ -77,6 +85,7 @@ public class Editor extends Screen { private static final String TEXTUREATLAS_FILE_NAME = "Texture Atlas XML (generic)"; private static final String IMPORT_DIALOGUE = "import_something"; + private static final String ANIMATION_FILE_NAME = "Animation file"; private static Editor instance; private static UserPreferences preferences; @@ -380,6 +389,12 @@ public void importSpriteSheets() { } } + public void importAnimation() { + if (EditorFileChooser.showFileDialog(ANIMATION_FILE_NAME, Resources.strings().get(IMPORT_DIALOGUE, ANIMATION_FILE_NAME), false, "json") == JFileChooser.APPROVE_OPTION) { + this.processAnimation(EditorFileChooser.instance().getSelectedFile()); + } + } + public void importSounds() { if (EditorFileChooser.showFileDialog(AUDIO_FILE_NAME, Resources.strings().get(IMPORT_DIALOGUE, AUDIO_FILE_NAME), true, SoundFormat.getAllExtensions()) == JFileChooser.APPROVE_OPTION) { this.importSounds(EditorFileChooser.instance().getSelectedFiles()); @@ -488,6 +503,30 @@ private void processSpritesheets(SpritesheetImportPanel spritePanel) { this.loadSpriteSheets(sprites, true); } + /** + * Loads an animation (spritesheet with keyframes) in to the editor + * @param file - a json file, encoded by the asesprite export standard + */ + public void processAnimation(File file) { + try { + Animation animation = AsepriteHandler.importAnimation(file.getAbsolutePath()); + int[] keyFrames = animation.getKeyFrameDurations(); + SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); + spritesheetResource.setKeyframes(keyFrames); + Collection sprites = new ArrayList<>(Collections.singleton(spritesheetResource)); + for (SpritesheetResource info : sprites) { + Resources.spritesheets().getAll().removeIf(x -> x.getName().equals(info.getName() + "-preview")); + this.getGameFile().getSpriteSheets().removeIf(x -> x.getName().equals(info.getName())); + this.getGameFile().getSpriteSheets().add(info); + log.log(Level.INFO, "imported spritesheet {0}", new Object[]{info.getName()}); + } + this.loadSpriteSheets(sprites, true); + + } catch (IOException e) { + e.printStackTrace(); + } + } + public void importEmitters() { XmlImportDialog.importXml("Emitter", file -> { EmitterData emitter; @@ -793,4 +832,4 @@ private void gamefileLoaded() { callback.run(); } } -} \ No newline at end of file +} From cb98f95d7c3d972a16ab0eb108512073cfbadb6d Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Tue, 9 Mar 2021 23:04:54 +0100 Subject: [PATCH 02/16] Update Editor.java Changed imports --- .../de/gurkenlabs/utiliti/components/Editor.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java index fb1da5aec..6567da823 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java +++ b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java @@ -1,4 +1,5 @@ package de.gurkenlabs.utiliti.components; + import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; @@ -29,16 +30,6 @@ import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.bind.JAXBException; -import de.gurkenlabs.litiengine.Game; -import de.gurkenlabs.litiengine.environment.tilemap.IImageLayer; -import de.gurkenlabs.litiengine.environment.tilemap.ITileset; -import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterData; -import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterLoader; -import de.gurkenlabs.litiengine.gui.screens.Screen; -import de.gurkenlabs.litiengine.resources.ImageFormat; -import de.gurkenlabs.litiengine.resources.ResourceBundle; -import de.gurkenlabs.litiengine.resources.Resources; -import de.gurkenlabs.litiengine.resources.SoundFormat; import de.gurkenlabs.litiengine.Game; import de.gurkenlabs.litiengine.environment.tilemap.IImageLayer; import de.gurkenlabs.litiengine.environment.tilemap.ITileset; @@ -47,9 +38,12 @@ import de.gurkenlabs.litiengine.environment.tilemap.xml.TmxMap; import de.gurkenlabs.litiengine.graphics.Spritesheet; import de.gurkenlabs.litiengine.graphics.TextRenderer; +import de.gurkenlabs.litiengine.graphics.animation.Animation; +import de.gurkenlabs.litiengine.graphics.animation.AsepriteHandler; import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterData; import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterLoader; import de.gurkenlabs.litiengine.gui.screens.Screen; +import de.gurkenlabs.litiengine.resources.ImageFormat; import de.gurkenlabs.litiengine.resources.ResourceBundle; import de.gurkenlabs.litiengine.resources.Resources; import de.gurkenlabs.litiengine.resources.SoundFormat; From eccad3c1c97d1b17f4d119a003f6d31718b3a76b Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Tue, 9 Mar 2021 23:25:15 +0100 Subject: [PATCH 03/16] Update ResourcesMenu.java Added UI elements to resourcemenu for import --- .../de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java b/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java index 3107eb7fe..1531483ae 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java +++ b/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java @@ -54,6 +54,10 @@ public ResourcesMenu() { exportSpriteSheets.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK)); exportSpriteSheets.addActionListener(a -> Editor.instance().exportSpriteSheets()); + JMenuItem importAnimation = new JMenuItem(Resources.strings().get("menu_assets_importAnimation")); + importAnimation.addActionListener(a -> Editor.instance().importAnimation()); + importAnimation.setEnabled(false); + Editor.instance().onLoaded(() -> { importSpriteFile.setEnabled(Editor.instance().getCurrentResourceFile() != null); importSprite.setEnabled(Editor.instance().getCurrentResourceFile() != null); @@ -63,6 +67,7 @@ public ResourcesMenu() { importTilesets.setEnabled(Editor.instance().getCurrentResourceFile() != null); importSounds.setEnabled(Editor.instance().getCurrentResourceFile() != null); exportSpriteSheets.setEnabled(Editor.instance().getCurrentResourceFile() != null); + importAnimation.setEnabled(Editor.instance().getCurrentResourceFile() != null); }); this.add(importSprite); @@ -72,7 +77,8 @@ public ResourcesMenu() { this.add(importBlueprints); this.add(importTilesets); this.add(importSounds); - this.addSeparator(); + this.add(importAnimation); + this.addSeparator(); this.add(exportSpriteSheets); this.add(compress); } From 772565a917095ec5173c0c8cbcbb13eb973b2467 Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 09:10:18 +0100 Subject: [PATCH 04/16] Update AssetPanelItem.java Integration for export. Changed if else statements to switch --- .../utiliti/swing/AssetPanelItem.java | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java index 240fd1b9c..f6572d55a 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java +++ b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java @@ -11,6 +11,8 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; +import java.io.FileWriter; +import java.io.Writer; import java.io.FileOutputStream; import java.io.IOException; import java.util.Collection; @@ -48,7 +50,9 @@ import de.gurkenlabs.litiengine.environment.tilemap.xml.MapObject; import de.gurkenlabs.litiengine.environment.tilemap.xml.Tileset; import de.gurkenlabs.litiengine.graphics.Spritesheet; +import de.gurkenlabs.litiengine.graphics.animation.AsepriteHandler; import de.gurkenlabs.litiengine.graphics.emitters.xml.EmitterData; +import de.gurkenlabs.litiengine.graphics.animation.AsepriteHandler; import de.gurkenlabs.litiengine.resources.ImageFormat; import de.gurkenlabs.litiengine.resources.Resources; import de.gurkenlabs.litiengine.resources.SoundFormat; @@ -441,20 +445,47 @@ private void exportSpritesheet() { chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); chooser.setDialogType(JFileChooser.SAVE_DIALOG); chooser.setDialogTitle("Export Spritesheet"); - if (answer == 0) { - XmlExportDialog.export(spriteSheetInfo, "Spritesheet", spriteSheetInfo.getName()); - } else if (answer == 1) { - FileFilter filter = new FileNameExtensionFilter(format.toString() + " - Image", format.toString()); - chooser.setFileFilter(filter); - chooser.addChoosableFileFilter(filter); - chooser.setSelectedFile(new File(spriteSheetInfo.getName() + format.toFileExtension())); - - int result = chooser.showSaveDialog(Game.window().getRenderComponent()); - if (result == JFileChooser.APPROVE_OPTION) { - ImageSerializer.saveImage(chooser.getSelectedFile().toString(), sprite.getImage(), format); - log.log(Level.INFO, "exported spritesheet {0} to {1}", new Object[] { spriteSheetInfo.getName(), chooser.getSelectedFile() }); - } - } + switch (answer) { + case 0: { + XmlExportDialog.export(spriteSheetInfo, "Spritesheet", spriteSheetInfo.getName()); + break; + } + case 1: { + FileFilter filter = new FileNameExtensionFilter(format.toString() + " - Image", format.toString()); + chooser.setFileFilter(filter); + chooser.addChoosableFileFilter(filter); + chooser.setSelectedFile(new File(spriteSheetInfo.getName() + format.toFileExtension())); + + int result = chooser.showSaveDialog(Game.window().getRenderComponent()); + if (result == JFileChooser.APPROVE_OPTION) { + ImageSerializer.saveImage(chooser.getSelectedFile().toString(), sprite.getImage(), format); + log.log(Level.INFO, "exported spritesheet {0} to {1}", new Object[]{spriteSheetInfo.getName(), chooser.getSelectedFile()}); + } + break; + } + case 2: { + FileFilter filter = new FileNameExtensionFilter(".json" + " - " + "Spritesheet" + " JSON", "json"); + chooser.setFileFilter(filter); + chooser.addChoosableFileFilter(filter); + chooser.setSelectedFile(new File(spriteSheetInfo.getName() + "." + "json")); + + int result = chooser.showSaveDialog(Game.window().getRenderComponent()); + if (result == JFileChooser.APPROVE_OPTION) { + String fileNameWithExtension = chooser.getSelectedFile().toString(); + if (!fileNameWithExtension.endsWith(".json")) { + fileNameWithExtension += ".json"; + } + String json = AsepriteHandler.exportAnimation(spriteSheetInfo); + try (Writer writer = new FileWriter(fileNameWithExtension)) { + writer.write(json); + log.log(Level.INFO, "Exported {0} {1} to {2}", new Object[]{"Spritesheet", spriteSheetInfo.getName(), fileNameWithExtension}); + } catch (IOException e) { + e.printStackTrace(); + } + } + break; + } + } } catch (IOException e) { log.log(Level.SEVERE, e.getMessage(), e); } From 61878d8ce2be84bf4422495a9e56831075b5d1b3 Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 09:24:53 +0100 Subject: [PATCH 05/16] Update AsepriteHandler.java Added static modifiers --- .../litiengine/graphics/animation/AsepriteHandler.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java index e27e61a8a..ff1feef90 100644 --- a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java +++ b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java @@ -193,7 +193,7 @@ public ExportAnimationException(String message) { * * @param spritesheetResource the animation object to export */ - public String exportAnimation(SpritesheetResource spritesheetResource){ + public static String exportAnimation(SpritesheetResource spritesheetResource){ String json = createJson(spritesheetResource); return json; @@ -205,7 +205,7 @@ public String exportAnimation(SpritesheetResource spritesheetResource){ * @param spritesheetResource spritesheetResource object to export as json. * @return the json as a string. */ - private String createJson(SpritesheetResource spritesheetResource){ + private static String createJson(SpritesheetResource spritesheetResource){ Spritesheet spritesheet = Resources.spritesheets().load(spritesheetResource); assert spritesheet != null; int[] keyframes = Resources.spritesheets().getCustomKeyFrameDurations(spritesheet); @@ -286,8 +286,8 @@ private String createJson(SpritesheetResource spritesheetResource){ /** * Frames class for Aseprite json structure. */ - private class Frames { transient String name; + private static class Frames { Map frame; boolean rotated; boolean trimmed; @@ -319,7 +319,7 @@ public Frames(String name, Map frame, boolean rotated, boolean /** * Meta data class for Aseprite json structure. */ - private class Meta { + private static class Meta { String app; String version; String image; @@ -352,7 +352,7 @@ public Meta(String app, String version, String image, String format, Map Date: Wed, 10 Mar 2021 09:27:57 +0100 Subject: [PATCH 06/16] Update AsepriteHandler.java Forgot a field --- .../litiengine/graphics/animation/AsepriteHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java index ff1feef90..63e465bbd 100644 --- a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java +++ b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java @@ -286,9 +286,9 @@ private static String createJson(SpritesheetResource spritesheetResource){ /** * Frames class for Aseprite json structure. */ - transient String name; private static class Frames { - Map frame; + transient String name; + Map frame; boolean rotated; boolean trimmed; Map spriteSourceSize; From 4336bc9993a82d4a5b99326dad28bcd7b800a49a Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 09:39:55 +0100 Subject: [PATCH 07/16] Update strings.properties Added string resource for import animation --- utiliti/localization/strings.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/utiliti/localization/strings.properties b/utiliti/localization/strings.properties index 3f08e7d9b..d875be8e4 100644 --- a/utiliti/localization/strings.properties +++ b/utiliti/localization/strings.properties @@ -58,6 +58,7 @@ menu_assets_importBlueprints=Import Blueprints... menu_assets_importTilesets=Import Tilesets... menu_assets_importSounds=Import Sounds... menu_assets_editSprite=Edit Spritesheet(s) +menu_assets_importAnimation=Import Animation... menu_help=Help menu_help_utiliti=utiLITI From 34d8a019002ff31779efe4242e06f0d4798b7895 Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 09:54:42 +0100 Subject: [PATCH 08/16] Update AssetPanelItem.java Added json option --- utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java index f6572d55a..d7b3219ad 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java +++ b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java @@ -435,7 +435,7 @@ private void exportSpritesheet() { ImageFormat format = sprite.getImageFormat() != ImageFormat.UNSUPPORTED ? sprite.getImageFormat() : ImageFormat.PNG; - Object[] options = { ".xml", format.toFileExtension() }; + Object[] options = { ".xml", format.toFileExtension(), ".json"}; int answer = JOptionPane.showOptionDialog(Game.window().getRenderComponent(), "Select an export format:", "Export Spritesheet", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); try { From 8581bbc7e64110e1280554dc68b6535cb166dfc4 Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 10:03:13 +0100 Subject: [PATCH 09/16] Update AsepriteHandler.java Ran autoformat --- .../graphics/animation/AsepriteHandler.java | 675 +++++++++--------- 1 file changed, 334 insertions(+), 341 deletions(-) diff --git a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java index 63e465bbd..bfebda2da 100644 --- a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java +++ b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java @@ -29,345 +29,338 @@ /** * Offers an interface to import Aseprite JSON export format. * Note: requires animation key frames to have same dimensions to support internal animation format. - * */ + */ public class AsepriteHandler { - - /** - * Thrown to indicate error when importing Aseprite JSON format. - * */ - public static class ImportAnimationException extends Error { - public ImportAnimationException(String message) { - super(message); - } - } - - /** - * Imports an Aseprite animation (.json + sprite sheet). - * Note: searches for sprite sheet path through .json metadata, specifically 'image' element. This should be an absolute path in system. - * - * @param jsonPath path (including filename) to Aseprite JSON. - * - * @return Animation object represented by each key frame in Aseprite sprite sheet. - * */ - public static Animation importAnimation(String jsonPath) throws IOException, FileNotFoundException, AsepriteHandler.ImportAnimationException { - - JsonElement rootElement = null; - try { rootElement = getRootJsonElement(jsonPath); } - catch(FileNotFoundException e) { - throw new FileNotFoundException("FileNotFoundException: Could not find .json file " + jsonPath); - } - - String spriteSheetPath = getSpriteSheetPath(rootElement); - File spriteSheetFile = new File(spriteSheetPath); - if(!spriteSheetFile.exists()) { - throw new FileNotFoundException("FileNotFoundException: Could not find sprite sheet file. " + - "Expected location is 'image' in .json metadata, which evaluates to: " + spriteSheetPath); - } - - Dimension keyFrameDimensions = getKeyFrameDimensions(rootElement); - if(areKeyFramesSameDimensions(rootElement, keyFrameDimensions)) { - - BufferedImage image = null; - try { image = ImageIO.read(spriteSheetFile); } - catch(IOException e) { - throw new IOException("IOException: Could not write sprite sheet data to BufferedImage object."); - } - - Spritesheet spriteSheet = new Spritesheet(image, - spriteSheetPath, - (int)keyFrameDimensions.getWidth(), - (int)keyFrameDimensions.getHeight()); - - return new Animation(spriteSheet, false, getKeyFrameDurations(rootElement)); - } - - throw new AsepriteHandler.ImportAnimationException("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions."); - } - - /** - * @param jsonPath path (including filename) to Aseprite .json file. - * - * @return root element of JSON data. - * */ - private static JsonElement getRootJsonElement(String jsonPath) throws FileNotFoundException { - - File jsonFile = new File(jsonPath); - - try { - JsonElement rootElement = JsonParser.parseReader(new FileReader(jsonFile)); - return rootElement; - } - catch(FileNotFoundException e) { throw e; } - } - - /** - * @param rootElement root element of JSON data. - * - * @return path (including filename) to animation sprite sheet. - * */ - private static String getSpriteSheetPath(JsonElement rootElement) { - - JsonElement metaData = rootElement.getAsJsonObject().get("meta"); - String spriteSheetPath = metaData.getAsJsonObject().get("image").getAsString(); - - return spriteSheetPath; - } - - /** - * @param rootElement root element of JSON data. - * - * @return dimensions of first key frame. - * */ - private static Dimension getKeyFrameDimensions(JsonElement rootElement) { - - JsonElement frames = rootElement.getAsJsonObject().get("frames"); - - JsonObject firstFrameObject = frames.getAsJsonObject().entrySet().iterator().next().getValue().getAsJsonObject(); - JsonObject frameDimensions = firstFrameObject.get("sourceSize").getAsJsonObject(); - - int frameWidth = frameDimensions.get("w").getAsInt(); - int frameHeight = frameDimensions.get("h").getAsInt(); - - return new Dimension(frameWidth, frameHeight); - } - - /** - * @param rootElement root element of JSON data. - * @param expected expected dimensions of each key frame. - * - * @return true if key frames have same duration. - * */ - private static boolean areKeyFramesSameDimensions(JsonElement rootElement, Dimension expected) { - - JsonElement frames = rootElement.getAsJsonObject().get("frames"); - - for(Map.Entry entry : frames.getAsJsonObject().entrySet()) { - JsonObject frameObject = entry.getValue().getAsJsonObject(); - JsonObject frameDimensions = frameObject.get("sourceSize").getAsJsonObject(); - - int frameWidth = frameDimensions.get("w").getAsInt(); - int frameHeight = frameDimensions.get("h").getAsInt(); - - if(frameWidth != expected.getWidth() || frameHeight != expected.getHeight()) - return false; - } - - return true; - } - - /** - * @param rootElement root element of JSON data. - * - * @return integer array representing duration of each key frame. - * */ - public static int[] getKeyFrameDurations(JsonElement rootElement) { - - JsonElement frames = rootElement.getAsJsonObject().get("frames"); - - Set> keyFrameSet = frames.getAsJsonObject().entrySet(); - - int[] keyFrameDurations = new int[keyFrameSet.size()]; - - int frameIndex = 0; - for(Map.Entry entry : keyFrameSet) { - JsonObject frameObject = entry.getValue().getAsJsonObject(); - int frameDuration = frameObject.get("duration").getAsInt(); - keyFrameDurations[frameIndex++] = frameDuration; - } - - return keyFrameDurations; - } - - /** - * Error that is thrown by the export class - */ - public static class ExportAnimationException extends Error { - public ExportAnimationException(String message) { - super(message); - } - } - - /** - * Creates the json representation of an animation object and returns it. - * This is the public accesible function and can/should be changed to fit into the UI. - * - * @param spritesheetResource the animation object to export - */ - public static String exportAnimation(SpritesheetResource spritesheetResource){ - - String json = createJson(spritesheetResource); - return json; - } - - /** - * Creates the json representation of an animation object and returns it as a string. - * - * @param spritesheetResource spritesheetResource object to export as json. - * @return the json as a string. - */ - private static String createJson(SpritesheetResource spritesheetResource){ - Spritesheet spritesheet = Resources.spritesheets().load(spritesheetResource); - assert spritesheet != null; - int[] keyframes = Resources.spritesheets().getCustomKeyFrameDurations(spritesheet); - Frames[] frames = new Frames[keyframes.length]; - - if(frames.length != spritesheet.getTotalNumberOfSprites()){ - throw new ExportAnimationException("Different dimensions of keyframes and sprites in spritesheet"); - } - - // Build the frames object in the json - int numCol = spritesheet.getColumns(); - int numRows = spritesheet.getRows(); - int frameWidth = spritesheet.getSpriteWidth(); - int frameHeight = spritesheet.getSpriteHeight(); - - for(int i = 0; i < numRows; i++){ - for(int j = 0; j < numCol; j++){ - final int row = i; - final int col = j; - Map frame = new HashMap<>(){{ - put("x", (0 + col*frameWidth) ); - put("y", (0 + row*frameHeight) ); - put("w", frameWidth); - put("h", frameHeight); - }}; - Map spriteSourceSize = new HashMap<>(){{ - put("x", 0); - put("y", 0); - put("w", frameWidth); - put("h", frameHeight); - }}; - Map sourceSize = new HashMap<>(){{ - put("w", frameWidth); - put("h", frameHeight); - }}; - int duration = keyframes[i+j]; - String index = String.valueOf(i+j); - frames[i+j] = new Frames("frame " + index, - frame, - false, - false, - spriteSourceSize, - sourceSize, - duration); - } - } - - // Build the meta object in the json - int spritesheetWidth = frameWidth * numCol; - int spritesheetHeight = frameHeight * numRows; - Map size= new HashMap<>(){{ - put("w", spritesheetWidth); - put("h", spritesheetHeight); - }}; - String spritesheetName = spritesheet.getName(); - Layer[] layers = {new Layer("Layer",255,"normal")}; - Meta meta = new Meta("http://www.aseprite.org/", - "1.2.16.3-x64", - spritesheetName, - "RGBA8888", size, "1", layers); - - // Create the json as string - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - StringBuilder sb = new StringBuilder(); - - sb.append("{ \"frames\": {\n"); - for(int i = 0; i < frames.length; i++){ - String json = gson.toJson(frames[i]); - sb.append(" \"" + frames[i].name + "\": ").append(json).append(",\n"); - } - sb.append(" },\n"); - String json = gson.toJson(meta); - sb.append("\"meta\":").append(json).append("\n}"); - - return sb.toString(); - } - - /** - * Frames class for Aseprite json structure. - */ - private static class Frames { - transient String name; - Map frame; - boolean rotated; - boolean trimmed; - Map spriteSourceSize; - Map sourceSize; - int duration; - - /** - * - * @param name name of frame - * @param frame x, y, w, h on the substruction of the sprite in the spritesheet. - * @param rotated is the frame rotated? - * @param trimmed is the frame trimmed? - * @param spriteSourceSize how the sprite is trimmed. - * @param sourceSize the original sprite size. - * @param duration the duration of the frame - */ - public Frames(String name, Map frame, boolean rotated, boolean trimmed, Map spriteSourceSize, Map sourceSize, int duration){ - this.name = name; - this.frame = frame; - this.rotated = rotated; - this.trimmed = trimmed; - this.spriteSourceSize = spriteSourceSize; - this.sourceSize = sourceSize; - this.duration = duration; - } - } - - /** - * Meta data class for Aseprite json structure. - */ - private static class Meta { - String app; - String version; - String image; - String format; - Map size; - String scale; - Layer[] layers; - - /** - * - * @param app the application the json format comes from, in this case Aseprite. - * @param version Version of application. - * @param image filename of spritesheet. - * @param format color format of spritesheet image. - * @param size Size of spritesheet. - * @param scale Scale of spritesheet. - * @param layers Layers of spritesheet. - */ - public Meta(String app, String version, String image, String format, Map size, String scale, Layer[] layers){ - this.app = app; - this.version = version; - this.image = image; - this.format = format; - this.size = size; - this. scale = scale; - this.layers = layers; - } - } - - /** - * Layer class for Aseprite json structure. - */ - private static class Layer { - String name; - int opacity; - String blendMode; - - /** - * - * @param name Name of layer. - * @param opacity Opacity level of layer. - * @param blendMode Blendmode of layer. - */ - public Layer(String name, int opacity, String blendMode){ - this.name = name; - this.opacity = opacity; - this.blendMode = blendMode; - } - - } -} \ No newline at end of file + + /** + * Thrown to indicate error when importing Aseprite JSON format. + */ + public static class ImportAnimationException extends Error { + public ImportAnimationException(String message) { + super(message); + } + } + + /** + * Imports an Aseprite animation (.json + sprite sheet). + * Note: searches for sprite sheet path through .json metadata, specifically 'image' element. This should be an absolute path in system. + * + * @param jsonPath path (including filename) to Aseprite JSON. + * @return Animation object represented by each key frame in Aseprite sprite sheet. + */ + public static Animation importAnimation(String jsonPath) throws IOException, FileNotFoundException, AsepriteHandler.ImportAnimationException { + + JsonElement rootElement = null; + try { + rootElement = getRootJsonElement(jsonPath); + } catch (FileNotFoundException e) { + throw new FileNotFoundException("FileNotFoundException: Could not find .json file " + jsonPath); + } + + String spriteSheetPath = getSpriteSheetPath(rootElement); + File spriteSheetFile = new File(spriteSheetPath); + if (!spriteSheetFile.exists()) { + throw new FileNotFoundException("FileNotFoundException: Could not find sprite sheet file. " + + "Expected location is 'image' in .json metadata, which evaluates to: " + spriteSheetPath); + } + + Dimension keyFrameDimensions = getKeyFrameDimensions(rootElement); + if (areKeyFramesSameDimensions(rootElement, keyFrameDimensions)) { + + BufferedImage image = null; + try { + image = ImageIO.read(spriteSheetFile); + } catch (IOException e) { + throw new IOException("IOException: Could not write sprite sheet data to BufferedImage object."); + } + + Spritesheet spriteSheet = new Spritesheet(image, + spriteSheetPath, + (int) keyFrameDimensions.getWidth(), + (int) keyFrameDimensions.getHeight()); + + return new Animation(spriteSheet, false, getKeyFrameDurations(rootElement)); + } + + throw new AsepriteHandler.ImportAnimationException("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions."); + } + + /** + * @param jsonPath path (including filename) to Aseprite .json file. + * @return root element of JSON data. + */ + private static JsonElement getRootJsonElement(String jsonPath) throws FileNotFoundException { + + File jsonFile = new File(jsonPath); + + try { + JsonElement rootElement = JsonParser.parseReader(new FileReader(jsonFile)); + return rootElement; + } catch (FileNotFoundException e) { + throw e; + } + } + + /** + * @param rootElement root element of JSON data. + * @return path (including filename) to animation sprite sheet. + */ + private static String getSpriteSheetPath(JsonElement rootElement) { + + JsonElement metaData = rootElement.getAsJsonObject().get("meta"); + String spriteSheetPath = metaData.getAsJsonObject().get("image").getAsString(); + + return spriteSheetPath; + } + + /** + * @param rootElement root element of JSON data. + * @return dimensions of first key frame. + */ + private static Dimension getKeyFrameDimensions(JsonElement rootElement) { + + JsonElement frames = rootElement.getAsJsonObject().get("frames"); + + JsonObject firstFrameObject = frames.getAsJsonObject().entrySet().iterator().next().getValue().getAsJsonObject(); + JsonObject frameDimensions = firstFrameObject.get("sourceSize").getAsJsonObject(); + + int frameWidth = frameDimensions.get("w").getAsInt(); + int frameHeight = frameDimensions.get("h").getAsInt(); + + return new Dimension(frameWidth, frameHeight); + } + + /** + * @param rootElement root element of JSON data. + * @param expected expected dimensions of each key frame. + * @return true if key frames have same duration. + */ + private static boolean areKeyFramesSameDimensions(JsonElement rootElement, Dimension expected) { + + JsonElement frames = rootElement.getAsJsonObject().get("frames"); + + for (Map.Entry entry : frames.getAsJsonObject().entrySet()) { + JsonObject frameObject = entry.getValue().getAsJsonObject(); + JsonObject frameDimensions = frameObject.get("sourceSize").getAsJsonObject(); + + int frameWidth = frameDimensions.get("w").getAsInt(); + int frameHeight = frameDimensions.get("h").getAsInt(); + + if (frameWidth != expected.getWidth() || frameHeight != expected.getHeight()) + return false; + } + + return true; + } + + /** + * @param rootElement root element of JSON data. + * @return integer array representing duration of each key frame. + */ + public static int[] getKeyFrameDurations(JsonElement rootElement) { + + JsonElement frames = rootElement.getAsJsonObject().get("frames"); + + Set> keyFrameSet = frames.getAsJsonObject().entrySet(); + + int[] keyFrameDurations = new int[keyFrameSet.size()]; + + int frameIndex = 0; + for (Map.Entry entry : keyFrameSet) { + JsonObject frameObject = entry.getValue().getAsJsonObject(); + int frameDuration = frameObject.get("duration").getAsInt(); + keyFrameDurations[frameIndex++] = frameDuration; + } + + return keyFrameDurations; + } + + /** + * Error that is thrown by the export class + */ + public static class ExportAnimationException extends Error { + public ExportAnimationException(String message) { + super(message); + } + } + + /** + * Creates the json representation of an animation object and returns it. + * This is the public accesible function and can/should be changed to fit into the UI. + * + * @param spritesheetResource the animation object to export + */ + public static String exportAnimation(SpritesheetResource spritesheetResource) { + String json = createJson(spritesheetResource); + return json; + } + + /** + * Creates the json representation of an animation object and returns it as a string. + * + * @param spritesheetResource spritesheetResource object to export as json. + * @return the json as a string. + */ + private static String createJson(SpritesheetResource spritesheetResource) { + Spritesheet spritesheet = Resources.spritesheets().load(spritesheetResource); + assert spritesheet != null; + int[] keyframes = Resources.spritesheets().getCustomKeyFrameDurations(spritesheet); + Frames[] frames = new Frames[keyframes.length]; + + if (frames.length != spritesheet.getTotalNumberOfSprites()) { + throw new ExportAnimationException("Different dimensions of keyframes and sprites in spritesheet"); + } + + // Build the frames object in the json + int numCol = spritesheet.getColumns(); + int numRows = spritesheet.getRows(); + int frameWidth = spritesheet.getSpriteWidth(); + int frameHeight = spritesheet.getSpriteHeight(); + + for (int i = 0; i < numRows; i++) { + for (int j = 0; j < numCol; j++) { + final int row = i; + final int col = j; + Map frame = new HashMap<>() {{ + put("x", (0 + col * frameWidth)); + put("y", (0 + row * frameHeight)); + put("w", frameWidth); + put("h", frameHeight); + }}; + Map spriteSourceSize = new HashMap<>() {{ + put("x", 0); + put("y", 0); + put("w", frameWidth); + put("h", frameHeight); + }}; + Map sourceSize = new HashMap<>() {{ + put("w", frameWidth); + put("h", frameHeight); + }}; + int duration = keyframes[i + j]; + String index = String.valueOf(i + j); + frames[i + j] = new Frames("frame " + index, + frame, + false, + false, + spriteSourceSize, + sourceSize, + duration); + } + } + + // Build the meta object in the json + int spritesheetWidth = frameWidth * numCol; + int spritesheetHeight = frameHeight * numRows; + Map size = new HashMap<>() {{ + put("w", spritesheetWidth); + put("h", spritesheetHeight); + }}; + String spritesheetName = spritesheet.getName(); + Layer[] layers = {new Layer("Layer", 255, "normal")}; + Meta meta = new Meta("http://www.aseprite.org/", + "1.2.16.3-x64", + spritesheetName, + "RGBA8888", size, "1", layers); + + // Create the json as string + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + StringBuilder sb = new StringBuilder(); + + sb.append("{ \"frames\": {\n"); + for (int i = 0; i < frames.length; i++) { + String json = gson.toJson(frames[i]); + sb.append(" \"" + frames[i].name + "\": ").append(json).append(",\n"); + } + sb.append(" },\n"); + String json = gson.toJson(meta); + sb.append("\"meta\":").append(json).append("\n}"); + + return sb.toString(); + } + + /** + * Frames class for Aseprite json structure. + */ + private static class Frames { + transient String name; + Map frame; + boolean rotated; + boolean trimmed; + Map spriteSourceSize; + Map sourceSize; + int duration; + + /** + * @param name name of frame + * @param frame x, y, w, h on the substruction of the sprite in the spritesheet. + * @param rotated is the frame rotated? + * @param trimmed is the frame trimmed? + * @param spriteSourceSize how the sprite is trimmed. + * @param sourceSize the original sprite size. + * @param duration the duration of the frame + */ + public Frames(String name, Map frame, boolean rotated, boolean trimmed, Map spriteSourceSize, Map sourceSize, int duration) { + this.name = name; + this.frame = frame; + this.rotated = rotated; + this.trimmed = trimmed; + this.spriteSourceSize = spriteSourceSize; + this.sourceSize = sourceSize; + this.duration = duration; + } + } + + /** + * Meta data class for Aseprite json structure. + */ + private static class Meta { + String app; + String version; + String image; + String format; + Map size; + String scale; + Layer[] layers; + + /** + * @param app the application the json format comes from, in this case Aseprite. + * @param version Version of application. + * @param image filename of spritesheet. + * @param format color format of spritesheet image. + * @param size Size of spritesheet. + * @param scale Scale of spritesheet. + * @param layers Layers of spritesheet. + */ + public Meta(String app, String version, String image, String format, Map size, String scale, Layer[] layers) { + this.app = app; + this.version = version; + this.image = image; + this.format = format; + this.size = size; + this.scale = scale; + this.layers = layers; + } + } + + /** + * Layer class for Aseprite json structure. + */ + private static class Layer { + String name; + int opacity; + String blendMode; + + /** + * @param name Name of layer. + * @param opacity Opacity level of layer. + * @param blendMode Blendmode of layer. + */ + public Layer(String name, int opacity, String blendMode) { + this.name = name; + this.opacity = opacity; + this.blendMode = blendMode; + } + + } +} From 865a105a41cf9699806e8b9e75004d73ff3bf94f Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 10:06:04 +0100 Subject: [PATCH 10/16] Update AsepriteHandlerTests.java Ran autoformat --- .../animation/AsepriteHandlerTests.java | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java b/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java index 3c337b4f5..becc5781f 100644 --- a/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java +++ b/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java @@ -16,82 +16,82 @@ import de.gurkenlabs.litiengine.resources.SpritesheetResource; -public class AsepriteHandlerTests { - - /** - * Tests that Aseprite animation import works as expected when given valid input. - */ - @Test - public void importAsepriteAnimationTest() { - try { - Animation animation = AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001.json"); - assertEquals("Sprite-0001-sheet", animation.getName()); - assertEquals(300, animation.getTotalDuration()); - for(int keyFrameDuration : animation.getKeyFrameDurations()) - assertEquals(100, keyFrameDuration); - - Spritesheet spriteSheet = animation.getSpritesheet(); - assertEquals(32, spriteSheet.getSpriteHeight()); - assertEquals(32, spriteSheet.getSpriteWidth()); - assertEquals(3, spriteSheet.getTotalNumberOfSprites()); - assertEquals(1, spriteSheet.getRows()); - assertEquals(3, spriteSheet.getColumns()); - assertEquals(ImageFormat.PNG, spriteSheet.getImageFormat()); +import static org.junit.jupiter.api.Assertions.*; - BufferedImage image = spriteSheet.getImage(); - assertEquals(96, image.getWidth()); - assertEquals(32, image.getHeight()); - } - catch(FileNotFoundException e) { - fail(e.getMessage()); - } - catch(IOException e) { - fail(e.getMessage()); - } - catch(AsepriteHandler.ImportAnimationException e) { - fail(e.getMessage()); - } - } - /** - * Test that if AsepriteHandler.ImportAnimationException will be throwed if different frame dimensions are provided. - */ - @Test - public void ImportAnimationExceptionTest() { - - Throwable exception = assertThrows(ImportAnimationException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002.json")); - assertEquals("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions.", exception.getMessage()); - } - - /** - * Tests thrown FileNotFoundException when importing an Aseprite animation. - * - * 1.first, we test if FileNotFoundException would be throwed if .json file cannot be found. - * 2.then we test if FileNotFoundException would be throwed if spritesheet file cannot be found. - */ - @Test - public void FileNotFoundExceptionTest(){ - Throwable exception_withoutJsonFile = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json")); - assertEquals("FileNotFoundException: Could not find .json file tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json", exception_withoutJsonFile.getMessage()); - Throwable exception_withoutSpriteSheet = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0004.json")); - assertEquals("FileNotFoundException: Could not find sprite sheet file. Expected location is 'image' in .json metadata, which evaluates to: tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002-sheet.png", exception_withoutSpriteSheet.getMessage()); - } - - /** - * Test that just create a json and prints in to standard output. - */ - @Test - public void exportAnimationTest() { - String spritesheetPath = "tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001-sheet.png"; - BufferedImage image = new BufferedImage(96, 32, BufferedImage.TYPE_4BYTE_ABGR); - Spritesheet spritesheet = new Spritesheet(image, spritesheetPath, 32, 32); - Animation animation = new Animation(spritesheet, false, false, 2,2,2); - int[] keyFrames = animation.getKeyFrameDurations(); - SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); - spritesheetResource.setKeyframes(keyFrames); - - AsepriteHandler aseprite = new AsepriteHandler(); - String result = aseprite.exportAnimation(spritesheetResource); - System.out.println(result); - } +public class AsepriteHandlerTests { + + /** + * Tests that Aseprite animation import works as expected when given valid input. + */ + @Test + public void importAsepriteAnimationTest() { + try { + Animation animation = AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001.json"); + assertEquals("Sprite-0001-sheet", animation.getName()); + assertEquals(300, animation.getTotalDuration()); + for (int keyFrameDuration : animation.getKeyFrameDurations()) + assertEquals(100, keyFrameDuration); + + Spritesheet spriteSheet = animation.getSpritesheet(); + assertEquals(32, spriteSheet.getSpriteHeight()); + assertEquals(32, spriteSheet.getSpriteWidth()); + assertEquals(3, spriteSheet.getTotalNumberOfSprites()); + assertEquals(1, spriteSheet.getRows()); + assertEquals(3, spriteSheet.getColumns()); + assertEquals(ImageFormat.PNG, spriteSheet.getImageFormat()); + + BufferedImage image = spriteSheet.getImage(); + assertEquals(96, image.getWidth()); + assertEquals(32, image.getHeight()); + } catch (FileNotFoundException e) { + fail(e.getMessage()); + } catch (IOException e) { + fail(e.getMessage()); + } catch (AsepriteHandler.ImportAnimationException e) { + fail(e.getMessage()); + } + } + + /** + * Test that if AsepriteHandler.ImportAnimationException will be throwed if different frame dimensions are provided. + */ + @Test + public void ImportAnimationExceptionTest() { + + Throwable exception = assertThrows(ImportAnimationException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002.json")); + assertEquals("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions.", exception.getMessage()); + } + + /** + * Tests thrown FileNotFoundException when importing an Aseprite animation. + *

+ * 1.first, we test if FileNotFoundException would be throwed if .json file cannot be found. + * 2.then we test if FileNotFoundException would be throwed if spritesheet file cannot be found. + */ + @Test + public void FileNotFoundExceptionTest() { + Throwable exception_withoutJsonFile = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json")); + assertEquals("FileNotFoundException: Could not find .json file tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json", exception_withoutJsonFile.getMessage()); + Throwable exception_withoutSpriteSheet = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0004.json")); + assertEquals("FileNotFoundException: Could not find sprite sheet file. Expected location is 'image' in .json metadata, which evaluates to: tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002-sheet.png", exception_withoutSpriteSheet.getMessage()); + } + + /** + * Test that just create a json and prints in to standard output. + */ + @Test + public void exportAnimationTest() { + String spritesheetPath = "tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001-sheet.png"; + BufferedImage image = new BufferedImage(96, 32, BufferedImage.TYPE_4BYTE_ABGR); + Spritesheet spritesheet = new Spritesheet(image, spritesheetPath, 32, 32); + Animation animation = new Animation(spritesheet, false, false, 2, 2, 2); + int[] keyFrames = animation.getKeyFrameDurations(); + SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); + spritesheetResource.setKeyframes(keyFrames); + + AsepriteHandler aseprite = new AsepriteHandler(); + String result = aseprite.exportAnimation(spritesheetResource); + System.out.println(result); + } } From 27942cc9b9e8b05b0df148f79c2146da5d00bbe2 Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 13:39:40 +0100 Subject: [PATCH 11/16] Merge Changes from Issue/321 --- .../graphics/animation/AsepriteHandler.java | 56 +++++++++++++++++-- .../animation/AsepriteHandlerTests.java | 2 +- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java index bfebda2da..8434724e5 100644 --- a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java +++ b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java @@ -43,7 +43,6 @@ public ImportAnimationException(String message) { /** * Imports an Aseprite animation (.json + sprite sheet). - * Note: searches for sprite sheet path through .json metadata, specifically 'image' element. This should be an absolute path in system. * * @param jsonPath path (including filename) to Aseprite JSON. * @return Animation object represented by each key frame in Aseprite sprite sheet. @@ -57,11 +56,11 @@ public static Animation importAnimation(String jsonPath) throws IOException, Fil throw new FileNotFoundException("FileNotFoundException: Could not find .json file " + jsonPath); } - String spriteSheetPath = getSpriteSheetPath(rootElement); - File spriteSheetFile = new File(spriteSheetPath); - if (!spriteSheetFile.exists()) { + File spriteSheetFile = null; + try { spriteSheetFile = getSpriteSheetFile(rootElement, jsonPath); } + catch(FileNotFoundException e) { throw new FileNotFoundException("FileNotFoundException: Could not find sprite sheet file. " + - "Expected location is 'image' in .json metadata, which evaluates to: " + spriteSheetPath); + "Expected location is 'image' in .json metadata, or same folder as .json file."); } Dimension keyFrameDimensions = getKeyFrameDimensions(rootElement); @@ -75,7 +74,7 @@ public static Animation importAnimation(String jsonPath) throws IOException, Fil } Spritesheet spriteSheet = new Spritesheet(image, - spriteSheetPath, + spriteSheetFile.getPath().toString(), (int) keyFrameDimensions.getWidth(), (int) keyFrameDimensions.getHeight()); @@ -113,6 +112,51 @@ private static String getSpriteSheetPath(JsonElement rootElement) { return spriteSheetPath; } + /** + * Searches for sprite sheet path through .json metadata, or same folder as .json file. + * @param rootElement root element of JSON data. + * @param jsonPath path (including filename) to .json Aseprite file. + * + * @return sprite sheet file if it can be found, else an exception is thrown. + * */ + private static File getSpriteSheetFile(JsonElement rootElement, String jsonPath) throws FileNotFoundException { + + //try searching path supplied in .json data + JsonElement metaData = rootElement.getAsJsonObject().get("meta"); + String spriteSheetPath = metaData.getAsJsonObject().get("image").getAsString(); + + File spriteSheetFile = new File(spriteSheetPath); + + if(spriteSheetFile.exists()) + return spriteSheetFile; + + //try searching local directory + Path jsonFilePath = Paths.get(jsonPath); + String dirPath = jsonFilePath.getParent().toString(); + String fileName1 = jsonFilePath.getFileName().toString(); + String alternative1 = fileName1.substring(0, fileName1.lastIndexOf(".")); //same file name as .json + + Path spriteSheetFilePath = Paths.get(spriteSheetPath); + String fileName2 = spriteSheetFilePath.getFileName().toString(); + String alternative2 = fileName2.substring(0, fileName2.lastIndexOf(".")); //same file name as 'image' element + + List suffixes = Arrays.asList(".png", ".jpg", ".jpeg"); + for(String suffix : suffixes) { + + String alternativeFile1 = dirPath + "/" + alternative1 + suffix; + spriteSheetFile = new File(alternativeFile1); + if(spriteSheetFile.exists()) + return spriteSheetFile; + + String alternativeFile2 = dirPath + "/" + alternative2 + suffix; + spriteSheetFile = new File(alternativeFile2); + if(spriteSheetFile.exists()) + return spriteSheetFile; + } + + throw new FileNotFoundException(); + } + /** * @param rootElement root element of JSON data. * @return dimensions of first key frame. diff --git a/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java b/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java index becc5781f..c8a5f2ca6 100644 --- a/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java +++ b/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java @@ -74,7 +74,7 @@ public void FileNotFoundExceptionTest() { Throwable exception_withoutJsonFile = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json")); assertEquals("FileNotFoundException: Could not find .json file tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json", exception_withoutJsonFile.getMessage()); Throwable exception_withoutSpriteSheet = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0004.json")); - assertEquals("FileNotFoundException: Could not find sprite sheet file. Expected location is 'image' in .json metadata, which evaluates to: tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002-sheet.png", exception_withoutSpriteSheet.getMessage()); + assertEquals("FileNotFoundException: Could not find sprite sheet file. Expected location is 'image' in .json metadata, or same folder as .json file.", exception_withoutSpriteSheet.getMessage()); } /** From 5597332103ce62090849cdabb165cda48ab6f20b Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 13:46:53 +0100 Subject: [PATCH 12/16] Update AsepriteHandler.java --- .../litiengine/graphics/animation/AsepriteHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java index 8434724e5..2c01da6f3 100644 --- a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java +++ b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java @@ -12,6 +12,9 @@ import java.util.HashMap; import java.util.List; import javax.imageio.ImageIO; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; import com.google.gson.Gson; import com.google.gson.GsonBuilder; From bbf14578f9465a03f9bdae21a1b1d3f1b94fceef Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 14:55:18 +0100 Subject: [PATCH 13/16] Added caught exceptions catches the right exceptions and logs them --- utiliti/src/de/gurkenlabs/utiliti/components/Editor.java | 8 ++++---- .../src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java index 6567da823..74b839a59 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java +++ b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java @@ -516,10 +516,10 @@ public void processAnimation(File file) { } this.loadSpriteSheets(sprites, true); - } catch (IOException e) { - e.printStackTrace(); - } - } + } catch (AsepriteHandler.ImportAnimationException | IOException e) { + log.log(Level.SEVERE, e.getMessage(), e); + } + } public void importEmitters() { XmlImportDialog.importXml("Emitter", file -> { diff --git a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java index d7b3219ad..b8d26f867 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java +++ b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java @@ -486,7 +486,7 @@ private void exportSpritesheet() { break; } } - } catch (IOException e) { + } catch (AsepriteHandler.ExportAnimationException | IOException e) { log.log(Level.SEVERE, e.getMessage(), e); } } From 00842ccb8afa6fa03402cc4b1158d6c6d4c7e3cf Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 15:08:59 +0100 Subject: [PATCH 14/16] Fix formatting --- .../graphics/animation/AsepriteHandler.java | 691 +++++++++--------- .../animation/AsepriteHandlerTests.java | 148 ++-- .../gurkenlabs/utiliti/components/Editor.java | 48 +- .../utiliti/swing/menus/ResourcesMenu.java | 10 +- 4 files changed, 450 insertions(+), 447 deletions(-) diff --git a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java index dcea9e172..81a92200e 100644 --- a/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java +++ b/src/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandler.java @@ -34,378 +34,381 @@ * Note: requires animation key frames to have same dimensions to support internal animation format. */ public class AsepriteHandler { - - /** - * Thrown to indicate error when importing Aseprite JSON format. - */ - public static class ImportAnimationException extends Error { - public ImportAnimationException(String message) { - super(message); - } - } - - /** - * Imports an Aseprite animation (.json + sprite sheet). - * - * @param jsonPath path (including filename) to Aseprite JSON. - * @return Animation object represented by each key frame in Aseprite sprite sheet. - */ - public static Animation importAnimation(String jsonPath) throws IOException, FileNotFoundException, AsepriteHandler.ImportAnimationException { - - JsonElement rootElement = null; - try { rootElement = getRootJsonElement(jsonPath); } - catch (FileNotFoundException e) { - throw new FileNotFoundException("FileNotFoundException: Could not find .json file " + jsonPath); - } - + + /** + * Thrown to indicate error when importing Aseprite JSON format. + */ + public static class ImportAnimationException extends Error { + public ImportAnimationException(String message) { + super(message); + } + } + + /** + * Imports an Aseprite animation (.json + sprite sheet). + * + * @param jsonPath path (including filename) to Aseprite JSON. + * @return Animation object represented by each key frame in Aseprite sprite sheet. + */ + public static Animation importAnimation(String jsonPath) throws IOException, FileNotFoundException, AsepriteHandler.ImportAnimationException { + + JsonElement rootElement = null; + try { + rootElement = getRootJsonElement(jsonPath); + } catch (FileNotFoundException e) { + throw new FileNotFoundException("FileNotFoundException: Could not find .json file " + jsonPath); + } + File spriteSheetFile = null; - try { spriteSheetFile = getSpriteSheetFile(rootElement, jsonPath); } - catch(FileNotFoundException e) { + try { + spriteSheetFile = getSpriteSheetFile(rootElement, jsonPath); + } catch (FileNotFoundException e) { throw new FileNotFoundException("FileNotFoundException: Could not find sprite sheet file. " + - "Expected location is 'image' in .json metadata, or same folder as .json file."); + "Expected location is 'image' in .json metadata, or same folder as .json file."); } - - Dimension keyFrameDimensions = getKeyFrameDimensions(rootElement); - if (areKeyFramesSameDimensions(rootElement, keyFrameDimensions)) { - - BufferedImage image = null; - try { image = ImageIO.read(spriteSheetFile); } - catch (IOException e) { - throw new IOException("IOException: Could not write sprite sheet data to BufferedImage object."); - } - - Spritesheet spriteSheet = new Spritesheet(image, - spriteSheetFile.getPath().toString(), - (int) keyFrameDimensions.getWidth(), - (int) keyFrameDimensions.getHeight()); - - return new Animation(spriteSheet, false, getKeyFrameDurations(rootElement)); - } - - throw new AsepriteHandler.ImportAnimationException("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions."); - } - - /** - * @param jsonPath path (including filename) to Aseprite .json file. - * @return root element of JSON data. - */ - private static JsonElement getRootJsonElement(String jsonPath) throws FileNotFoundException { - - File jsonFile = new File(jsonPath); - - try { - JsonElement rootElement = JsonParser.parseReader(new FileReader(jsonFile)); - return rootElement; - } catch (FileNotFoundException e) { - throw e; - } - } - - /** - * @param rootElement root element of JSON data. - * @return path (including filename) to animation sprite sheet. - */ - private static String getSpriteSheetPath(JsonElement rootElement) { - - JsonElement metaData = rootElement.getAsJsonObject().get("meta"); - String spriteSheetPath = metaData.getAsJsonObject().get("image").getAsString(); - - return spriteSheetPath; - } - + + Dimension keyFrameDimensions = getKeyFrameDimensions(rootElement); + if (areKeyFramesSameDimensions(rootElement, keyFrameDimensions)) { + + BufferedImage image = null; + try { + image = ImageIO.read(spriteSheetFile); + } catch (IOException e) { + throw new IOException("IOException: Could not write sprite sheet data to BufferedImage object."); + } + + Spritesheet spriteSheet = new Spritesheet(image, + spriteSheetFile.getPath().toString(), + (int) keyFrameDimensions.getWidth(), + (int) keyFrameDimensions.getHeight()); + + return new Animation(spriteSheet, false, getKeyFrameDurations(rootElement)); + } + + throw new AsepriteHandler.ImportAnimationException("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions."); + } + + /** + * @param jsonPath path (including filename) to Aseprite .json file. + * @return root element of JSON data. + */ + private static JsonElement getRootJsonElement(String jsonPath) throws FileNotFoundException { + + File jsonFile = new File(jsonPath); + + try { + JsonElement rootElement = JsonParser.parseReader(new FileReader(jsonFile)); + return rootElement; + } catch (FileNotFoundException e) { + throw e; + } + } + /** - * Searches for sprite sheet path through .json metadata and same folder as .json file. * @param rootElement root element of JSON data. - * @param jsonPath path (including filename) to .json Aseprite file. + * @return path (including filename) to animation sprite sheet. + */ + private static String getSpriteSheetPath(JsonElement rootElement) { + + JsonElement metaData = rootElement.getAsJsonObject().get("meta"); + String spriteSheetPath = metaData.getAsJsonObject().get("image").getAsString(); + + return spriteSheetPath; + } + + /** + * Searches for sprite sheet path through .json metadata and same folder as .json file. * + * @param rootElement root element of JSON data. + * @param jsonPath path (including filename) to .json Aseprite file. * @return sprite sheet file if it can be found, else an exception is thrown. - * */ + */ private static File getSpriteSheetFile(JsonElement rootElement, String jsonPath) throws FileNotFoundException { - + //try searching path supplied in .json data JsonElement metaData = rootElement.getAsJsonObject().get("meta"); String spriteSheetPath = metaData.getAsJsonObject().get("image").getAsString(); - + File spriteSheetFile = new File(spriteSheetPath); - - if(spriteSheetFile.exists()) + + if (spriteSheetFile.exists()) return spriteSheetFile; - + //try searching local directory Path jsonFilePath = Paths.get(jsonPath); String dirPath = jsonFilePath.getParent().toString(); String fileName1 = jsonFilePath.getFileName().toString(); String alternative1 = fileName1.substring(0, fileName1.lastIndexOf(".")); //same file name as .json - + Path spriteSheetFilePath = Paths.get(spriteSheetPath); String fileName2 = spriteSheetFilePath.getFileName().toString(); String alternative2 = fileName2.substring(0, fileName2.lastIndexOf(".")); //same file name as 'image' element - + List suffixes = Arrays.asList(".png", ".jpg", ".jpeg"); - for(String suffix : suffixes) { - + for (String suffix : suffixes) { + String alternativeFile1 = dirPath + "/" + alternative1 + suffix; spriteSheetFile = new File(alternativeFile1); - if(spriteSheetFile.exists()) + if (spriteSheetFile.exists()) return spriteSheetFile; - + String alternativeFile2 = dirPath + "/" + alternative2 + suffix; spriteSheetFile = new File(alternativeFile2); - if(spriteSheetFile.exists()) + if (spriteSheetFile.exists()) return spriteSheetFile; } - + throw new FileNotFoundException(); } - - /** - * @param rootElement root element of JSON data. - * @return dimensions of first key frame. - */ - private static Dimension getKeyFrameDimensions(JsonElement rootElement) { - - JsonElement frames = rootElement.getAsJsonObject().get("frames"); - - JsonObject firstFrameObject = frames.getAsJsonObject().entrySet().iterator().next().getValue().getAsJsonObject(); - JsonObject frameDimensions = firstFrameObject.get("sourceSize").getAsJsonObject(); - - int frameWidth = frameDimensions.get("w").getAsInt(); - int frameHeight = frameDimensions.get("h").getAsInt(); - - return new Dimension(frameWidth, frameHeight); - } - - /** - * @param rootElement root element of JSON data. - * @param expected expected dimensions of each key frame. - * @return true if key frames have same duration. - */ - private static boolean areKeyFramesSameDimensions(JsonElement rootElement, Dimension expected) { - - JsonElement frames = rootElement.getAsJsonObject().get("frames"); - - for (Map.Entry entry : frames.getAsJsonObject().entrySet()) { - JsonObject frameObject = entry.getValue().getAsJsonObject(); - JsonObject frameDimensions = frameObject.get("sourceSize").getAsJsonObject(); - - int frameWidth = frameDimensions.get("w").getAsInt(); - int frameHeight = frameDimensions.get("h").getAsInt(); - - if (frameWidth != expected.getWidth() || frameHeight != expected.getHeight()) - return false; - } - - return true; - } - - /** - * @param rootElement root element of JSON data. - * @return integer array representing duration of each key frame. - */ - public static int[] getKeyFrameDurations(JsonElement rootElement) { - - JsonElement frames = rootElement.getAsJsonObject().get("frames"); - - Set> keyFrameSet = frames.getAsJsonObject().entrySet(); - - int[] keyFrameDurations = new int[keyFrameSet.size()]; - - int frameIndex = 0; - for (Map.Entry entry : keyFrameSet) { - JsonObject frameObject = entry.getValue().getAsJsonObject(); - int frameDuration = frameObject.get("duration").getAsInt(); - keyFrameDurations[frameIndex++] = frameDuration; - } - - return keyFrameDurations; - } - - /** - * Error that is thrown by the export class - */ - public static class ExportAnimationException extends Error { - public ExportAnimationException(String message) { - super(message); - } - } - - /** - * Creates the json representation of an animation object and returns it. - * This is the public accesible function and can/should be changed to fit into the UI. - * - * @param spritesheetResource the animation object to export - */ - public static String exportAnimation(SpritesheetResource spritesheetResource) { - String json = createJson(spritesheetResource); - return json; - } - - /** - * Creates the json representation of an animation object and returns it as a string. - * - * @param spritesheetResource spritesheetResource object to export as json. - * @return the json as a string. - */ - private static String createJson(SpritesheetResource spritesheetResource) { - Spritesheet spritesheet = Resources.spritesheets().load(spritesheetResource); - assert spritesheet != null; - int[] keyframes = Resources.spritesheets().getCustomKeyFrameDurations(spritesheet); - Frames[] frames = new Frames[keyframes.length]; - - if (frames.length != spritesheet.getTotalNumberOfSprites()) { - throw new ExportAnimationException("Different dimensions of keyframes and sprites in spritesheet"); - } - - // Build the frames object in the json - int numCol = spritesheet.getColumns(); - int numRows = spritesheet.getRows(); - int frameWidth = spritesheet.getSpriteWidth(); - int frameHeight = spritesheet.getSpriteHeight(); - - for (int i = 0; i < numRows; i++) { - for (int j = 0; j < numCol; j++) { - final int row = i; - final int col = j; - Map frame = new HashMap<>() {{ - put("x", (0 + col * frameWidth)); - put("y", (0 + row * frameHeight)); - put("w", frameWidth); - put("h", frameHeight); - }}; - Map spriteSourceSize = new HashMap<>() {{ - put("x", 0); - put("y", 0); - put("w", frameWidth); - put("h", frameHeight); - }}; - Map sourceSize = new HashMap<>() {{ - put("w", frameWidth); - put("h", frameHeight); - }}; - int duration = keyframes[i + j]; - String index = String.valueOf(i + j); - frames[i + j] = new Frames("frame " + index, - frame, - false, - false, - spriteSourceSize, - sourceSize, - duration); - } - } - - // Build the meta object in the json - int spritesheetWidth = frameWidth * numCol; - int spritesheetHeight = frameHeight * numRows; - Map size = new HashMap<>() {{ - put("w", spritesheetWidth); - put("h", spritesheetHeight); - }}; - String spritesheetName = spritesheet.getName(); - Layer[] layers = {new Layer("Layer", 255, "normal")}; - Meta meta = new Meta("http://www.aseprite.org/", - "1.2.16.3-x64", - spritesheetName, - "RGBA8888", size, "1", layers); - - // Create the json as string - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - StringBuilder sb = new StringBuilder(); - - sb.append("{ \"frames\": {\n"); - for (int i = 0; i < frames.length; i++) { - String json = gson.toJson(frames[i]); - sb.append(" \"" + frames[i].name + "\": ").append(json).append(",\n"); - } - sb.append(" },\n"); - String json = gson.toJson(meta); - sb.append("\"meta\":").append(json).append("\n}"); - - return sb.toString(); - } - - /** - * Frames class for Aseprite json structure. - */ - private static class Frames { - transient String name; - Map frame; - boolean rotated; - boolean trimmed; - Map spriteSourceSize; - Map sourceSize; - int duration; - - /** - * @param name name of frame - * @param frame x, y, w, h on the substruction of the sprite in the spritesheet. - * @param rotated is the frame rotated? - * @param trimmed is the frame trimmed? - * @param spriteSourceSize how the sprite is trimmed. - * @param sourceSize the original sprite size. - * @param duration the duration of the frame - */ - public Frames(String name, Map frame, boolean rotated, boolean trimmed, Map spriteSourceSize, Map sourceSize, int duration) { - this.name = name; - this.frame = frame; - this.rotated = rotated; - this.trimmed = trimmed; - this.spriteSourceSize = spriteSourceSize; - this.sourceSize = sourceSize; - this.duration = duration; - } - } - - /** - * Meta data class for Aseprite json structure. - */ - private static class Meta { - String app; - String version; - String image; - String format; - Map size; - String scale; - Layer[] layers; - - /** - * @param app the application the json format comes from, in this case Aseprite. - * @param version Version of application. - * @param image filename of spritesheet. - * @param format color format of spritesheet image. - * @param size Size of spritesheet. - * @param scale Scale of spritesheet. - * @param layers Layers of spritesheet. - */ - public Meta(String app, String version, String image, String format, Map size, String scale, Layer[] layers) { - this.app = app; - this.version = version; - this.image = image; - this.format = format; - this.size = size; - this.scale = scale; - this.layers = layers; - } - } - - /** - * Layer class for Aseprite json structure. - */ - private static class Layer { - String name; - int opacity; - String blendMode; - - /** - * @param name Name of layer. - * @param opacity Opacity level of layer. - * @param blendMode Blendmode of layer. - */ - public Layer(String name, int opacity, String blendMode) { - this.name = name; - this.opacity = opacity; - this.blendMode = blendMode; - } - - } -} \ No newline at end of file + + /** + * @param rootElement root element of JSON data. + * @return dimensions of first key frame. + */ + private static Dimension getKeyFrameDimensions(JsonElement rootElement) { + + JsonElement frames = rootElement.getAsJsonObject().get("frames"); + + JsonObject firstFrameObject = frames.getAsJsonObject().entrySet().iterator().next().getValue().getAsJsonObject(); + JsonObject frameDimensions = firstFrameObject.get("sourceSize").getAsJsonObject(); + + int frameWidth = frameDimensions.get("w").getAsInt(); + int frameHeight = frameDimensions.get("h").getAsInt(); + + return new Dimension(frameWidth, frameHeight); + } + + /** + * @param rootElement root element of JSON data. + * @param expected expected dimensions of each key frame. + * @return true if key frames have same duration. + */ + private static boolean areKeyFramesSameDimensions(JsonElement rootElement, Dimension expected) { + + JsonElement frames = rootElement.getAsJsonObject().get("frames"); + + for (Map.Entry entry : frames.getAsJsonObject().entrySet()) { + JsonObject frameObject = entry.getValue().getAsJsonObject(); + JsonObject frameDimensions = frameObject.get("sourceSize").getAsJsonObject(); + + int frameWidth = frameDimensions.get("w").getAsInt(); + int frameHeight = frameDimensions.get("h").getAsInt(); + + if (frameWidth != expected.getWidth() || frameHeight != expected.getHeight()) + return false; + } + + return true; + } + + /** + * @param rootElement root element of JSON data. + * @return integer array representing duration of each key frame. + */ + public static int[] getKeyFrameDurations(JsonElement rootElement) { + + JsonElement frames = rootElement.getAsJsonObject().get("frames"); + + Set> keyFrameSet = frames.getAsJsonObject().entrySet(); + + int[] keyFrameDurations = new int[keyFrameSet.size()]; + + int frameIndex = 0; + for (Map.Entry entry : keyFrameSet) { + JsonObject frameObject = entry.getValue().getAsJsonObject(); + int frameDuration = frameObject.get("duration").getAsInt(); + keyFrameDurations[frameIndex++] = frameDuration; + } + + return keyFrameDurations; + } + + /** + * Error that is thrown by the export class + */ + public static class ExportAnimationException extends Error { + public ExportAnimationException(String message) { + super(message); + } + } + + /** + * Creates the json representation of an animation object and returns it. + * This is the public accesible function and can/should be changed to fit into the UI. + * + * @param spritesheetResource the animation object to export + */ + public static String exportAnimation(SpritesheetResource spritesheetResource) { + String json = createJson(spritesheetResource); + return json; + } + + /** + * Creates the json representation of an animation object and returns it as a string. + * + * @param spritesheetResource spritesheetResource object to export as json. + * @return the json as a string. + */ + private static String createJson(SpritesheetResource spritesheetResource) { + Spritesheet spritesheet = Resources.spritesheets().load(spritesheetResource); + assert spritesheet != null; + int[] keyframes = Resources.spritesheets().getCustomKeyFrameDurations(spritesheet); + Frames[] frames = new Frames[keyframes.length]; + + if (frames.length != spritesheet.getTotalNumberOfSprites()) { + throw new ExportAnimationException("Different dimensions of keyframes and sprites in spritesheet"); + } + + // Build the frames object in the json + int numCol = spritesheet.getColumns(); + int numRows = spritesheet.getRows(); + int frameWidth = spritesheet.getSpriteWidth(); + int frameHeight = spritesheet.getSpriteHeight(); + + for (int i = 0; i < numRows; i++) { + for (int j = 0; j < numCol; j++) { + final int row = i; + final int col = j; + Map frame = new HashMap<>() {{ + put("x", (0 + col * frameWidth)); + put("y", (0 + row * frameHeight)); + put("w", frameWidth); + put("h", frameHeight); + }}; + Map spriteSourceSize = new HashMap<>() {{ + put("x", 0); + put("y", 0); + put("w", frameWidth); + put("h", frameHeight); + }}; + Map sourceSize = new HashMap<>() {{ + put("w", frameWidth); + put("h", frameHeight); + }}; + int duration = keyframes[i + j]; + String index = String.valueOf(i + j); + frames[i + j] = new Frames("frame " + index, + frame, + false, + false, + spriteSourceSize, + sourceSize, + duration); + } + } + + // Build the meta object in the json + int spritesheetWidth = frameWidth * numCol; + int spritesheetHeight = frameHeight * numRows; + Map size = new HashMap<>() {{ + put("w", spritesheetWidth); + put("h", spritesheetHeight); + }}; + String spritesheetName = spritesheet.getName(); + Layer[] layers = {new Layer("Layer", 255, "normal")}; + Meta meta = new Meta("http://www.aseprite.org/", + "1.2.16.3-x64", + spritesheetName, + "RGBA8888", size, "1", layers); + + // Create the json as string + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + StringBuilder sb = new StringBuilder(); + + sb.append("{ \"frames\": {\n"); + for (int i = 0; i < frames.length; i++) { + String json = gson.toJson(frames[i]); + sb.append(" \"" + frames[i].name + "\": ").append(json).append(",\n"); + } + sb.append(" },\n"); + String json = gson.toJson(meta); + sb.append("\"meta\":").append(json).append("\n}"); + + return sb.toString(); + } + + /** + * Frames class for Aseprite json structure. + */ + private static class Frames { + transient String name; + Map frame; + boolean rotated; + boolean trimmed; + Map spriteSourceSize; + Map sourceSize; + int duration; + + /** + * @param name name of frame + * @param frame x, y, w, h on the substruction of the sprite in the spritesheet. + * @param rotated is the frame rotated? + * @param trimmed is the frame trimmed? + * @param spriteSourceSize how the sprite is trimmed. + * @param sourceSize the original sprite size. + * @param duration the duration of the frame + */ + public Frames(String name, Map frame, boolean rotated, boolean trimmed, Map spriteSourceSize, Map sourceSize, int duration) { + this.name = name; + this.frame = frame; + this.rotated = rotated; + this.trimmed = trimmed; + this.spriteSourceSize = spriteSourceSize; + this.sourceSize = sourceSize; + this.duration = duration; + } + } + + /** + * Meta data class for Aseprite json structure. + */ + private static class Meta { + String app; + String version; + String image; + String format; + Map size; + String scale; + Layer[] layers; + + /** + * @param app the application the json format comes from, in this case Aseprite. + * @param version Version of application. + * @param image filename of spritesheet. + * @param format color format of spritesheet image. + * @param size Size of spritesheet. + * @param scale Scale of spritesheet. + * @param layers Layers of spritesheet. + */ + public Meta(String app, String version, String image, String format, Map size, String scale, Layer[] layers) { + this.app = app; + this.version = version; + this.image = image; + this.format = format; + this.size = size; + this.scale = scale; + this.layers = layers; + } + } + + /** + * Layer class for Aseprite json structure. + */ + private static class Layer { + String name; + int opacity; + String blendMode; + + /** + * @param name Name of layer. + * @param opacity Opacity level of layer. + * @param blendMode Blendmode of layer. + */ + public Layer(String name, int opacity, String blendMode) { + this.name = name; + this.opacity = opacity; + this.blendMode = blendMode; + } + + } +} diff --git a/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java b/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java index 766b7b04f..0961fb48c 100644 --- a/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java +++ b/tests/de/gurkenlabs/litiengine/graphics/animation/AsepriteHandlerTests.java @@ -18,78 +18,78 @@ import static org.junit.jupiter.api.Assertions.*; public class AsepriteHandlerTests { - - /** - * Tests that Aseprite animation import works as expected when given valid input. - */ - @Test - public void importAsepriteAnimationTest() { - try { - Animation animation = AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001.json"); - assertEquals("Sprite-0001-sheet", animation.getName()); - assertEquals(300, animation.getTotalDuration()); - for (int keyFrameDuration : animation.getKeyFrameDurations()) - assertEquals(100, keyFrameDuration); - - Spritesheet spriteSheet = animation.getSpritesheet(); - assertEquals(32, spriteSheet.getSpriteHeight()); - assertEquals(32, spriteSheet.getSpriteWidth()); - assertEquals(3, spriteSheet.getTotalNumberOfSprites()); - assertEquals(1, spriteSheet.getRows()); - assertEquals(3, spriteSheet.getColumns()); - assertEquals(ImageFormat.PNG, spriteSheet.getImageFormat()); - - BufferedImage image = spriteSheet.getImage(); - assertEquals(96, image.getWidth()); - assertEquals(32, image.getHeight()); - } catch (FileNotFoundException e) { - fail(e.getMessage()); - } catch (IOException e) { - fail(e.getMessage()); - } catch (AsepriteHandler.ImportAnimationException e) { - fail(e.getMessage()); - } - } - - /** - * Test that if AsepriteHandler.ImportAnimationException will be throwed if different frame dimensions are provided. - */ - @Test - public void ImportAnimationExceptionTest() { - - Throwable exception = assertThrows(ImportAnimationException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002.json")); - assertEquals("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions.", exception.getMessage()); - } - - /** - * Tests thrown FileNotFoundException when importing an Aseprite animation. - *

- * 1.first, we test if FileNotFoundException would be throwed if .json file cannot be found. - * 2.then we test if FileNotFoundException would be throwed if spritesheet file cannot be found. - */ - @Test - public void FileNotFoundExceptionTest() { - Throwable exception_withoutJsonFile = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json")); - assertEquals("FileNotFoundException: Could not find .json file tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json", exception_withoutJsonFile.getMessage()); - Throwable exception_withoutSpriteSheet = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0004.json")); - assertEquals("FileNotFoundException: Could not find sprite sheet file. Expected location is 'image' in .json metadata, or same folder as .json file.", exception_withoutSpriteSheet.getMessage()); - } - - /** - * Test that just create a json and prints in to standard output. - */ - @Test - public void exportAnimationTest() { - String spritesheetPath = "tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001-sheet.png"; - BufferedImage image = new BufferedImage(96, 32, BufferedImage.TYPE_4BYTE_ABGR); - Spritesheet spritesheet = new Spritesheet(image, spritesheetPath, 32, 32); - Animation animation = new Animation(spritesheet, false, false, 2, 2, 2); - int[] keyFrames = animation.getKeyFrameDurations(); - SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); - spritesheetResource.setKeyframes(keyFrames); - - AsepriteHandler aseprite = new AsepriteHandler(); - String result = aseprite.exportAnimation(spritesheetResource); - System.out.println(result); - } + + /** + * Tests that Aseprite animation import works as expected when given valid input. + */ + @Test + public void importAsepriteAnimationTest() { + try { + Animation animation = AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001.json"); + assertEquals("Sprite-0001-sheet", animation.getName()); + assertEquals(300, animation.getTotalDuration()); + for (int keyFrameDuration : animation.getKeyFrameDurations()) + assertEquals(100, keyFrameDuration); + + Spritesheet spriteSheet = animation.getSpritesheet(); + assertEquals(32, spriteSheet.getSpriteHeight()); + assertEquals(32, spriteSheet.getSpriteWidth()); + assertEquals(3, spriteSheet.getTotalNumberOfSprites()); + assertEquals(1, spriteSheet.getRows()); + assertEquals(3, spriteSheet.getColumns()); + assertEquals(ImageFormat.PNG, spriteSheet.getImageFormat()); + + BufferedImage image = spriteSheet.getImage(); + assertEquals(96, image.getWidth()); + assertEquals(32, image.getHeight()); + } catch (FileNotFoundException e) { + fail(e.getMessage()); + } catch (IOException e) { + fail(e.getMessage()); + } catch (AsepriteHandler.ImportAnimationException e) { + fail(e.getMessage()); + } + } + + /** + * Test that if AsepriteHandler.ImportAnimationException will be throwed if different frame dimensions are provided. + */ + @Test + public void ImportAnimationExceptionTest() { + + Throwable exception = assertThrows(ImportAnimationException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0002.json")); + assertEquals("AsepriteHandler.ImportAnimationException: animation key frames require same dimensions.", exception.getMessage()); + } + + /** + * Tests thrown FileNotFoundException when importing an Aseprite animation. + *

+ * 1.first, we test if FileNotFoundException would be throwed if .json file cannot be found. + * 2.then we test if FileNotFoundException would be throwed if spritesheet file cannot be found. + */ + @Test + public void FileNotFoundExceptionTest() { + Throwable exception_withoutJsonFile = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json")); + assertEquals("FileNotFoundException: Could not find .json file tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0003.json", exception_withoutJsonFile.getMessage()); + Throwable exception_withoutSpriteSheet = assertThrows(FileNotFoundException.class, () -> AsepriteHandler.importAnimation("tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0004.json")); + assertEquals("FileNotFoundException: Could not find sprite sheet file. Expected location is 'image' in .json metadata, or same folder as .json file.", exception_withoutSpriteSheet.getMessage()); + } + + /** + * Test that just create a json and prints in to standard output. + */ + @Test + public void exportAnimationTest() { + String spritesheetPath = "tests/de/gurkenlabs/litiengine/graphics/animation/aseprite_test_animations/Sprite-0001-sheet.png"; + BufferedImage image = new BufferedImage(96, 32, BufferedImage.TYPE_4BYTE_ABGR); + Spritesheet spritesheet = new Spritesheet(image, spritesheetPath, 32, 32); + Animation animation = new Animation(spritesheet, false, false, 2, 2, 2); + int[] keyFrames = animation.getKeyFrameDurations(); + SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); + spritesheetResource.setKeyframes(keyFrames); + + AsepriteHandler aseprite = new AsepriteHandler(); + String result = aseprite.exportAnimation(spritesheetResource); + System.out.println(result); + } } diff --git a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java index 74b839a59..47442d83d 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java +++ b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java @@ -384,10 +384,10 @@ public void importSpriteSheets() { } public void importAnimation() { - if (EditorFileChooser.showFileDialog(ANIMATION_FILE_NAME, Resources.strings().get(IMPORT_DIALOGUE, ANIMATION_FILE_NAME), false, "json") == JFileChooser.APPROVE_OPTION) { - this.processAnimation(EditorFileChooser.instance().getSelectedFile()); - } - } + if (EditorFileChooser.showFileDialog(ANIMATION_FILE_NAME, Resources.strings().get(IMPORT_DIALOGUE, ANIMATION_FILE_NAME), false, "json") == JFileChooser.APPROVE_OPTION) { + this.processAnimation(EditorFileChooser.instance().getSelectedFile()); + } + } public void importSounds() { if (EditorFileChooser.showFileDialog(AUDIO_FILE_NAME, Resources.strings().get(IMPORT_DIALOGUE, AUDIO_FILE_NAME), true, SoundFormat.getAllExtensions()) == JFileChooser.APPROVE_OPTION) { @@ -497,26 +497,26 @@ private void processSpritesheets(SpritesheetImportPanel spritePanel) { this.loadSpriteSheets(sprites, true); } - /** - * Loads an animation (spritesheet with keyframes) in to the editor - * @param file - a json file, encoded by the asesprite export standard - */ - public void processAnimation(File file) { - try { - Animation animation = AsepriteHandler.importAnimation(file.getAbsolutePath()); - int[] keyFrames = animation.getKeyFrameDurations(); - SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); - spritesheetResource.setKeyframes(keyFrames); - Collection sprites = new ArrayList<>(Collections.singleton(spritesheetResource)); - for (SpritesheetResource info : sprites) { - Resources.spritesheets().getAll().removeIf(x -> x.getName().equals(info.getName() + "-preview")); - this.getGameFile().getSpriteSheets().removeIf(x -> x.getName().equals(info.getName())); - this.getGameFile().getSpriteSheets().add(info); - log.log(Level.INFO, "imported spritesheet {0}", new Object[]{info.getName()}); - } - this.loadSpriteSheets(sprites, true); - - } catch (AsepriteHandler.ImportAnimationException | IOException e) { + /** + * Loads an animation (spritesheet with keyframes) in to the editor + * @param file - a json file, encoded by the asesprite export standard + */ + public void processAnimation(File file) { + try { + Animation animation = AsepriteHandler.importAnimation(file.getAbsolutePath()); + int[] keyFrames = animation.getKeyFrameDurations(); + SpritesheetResource spritesheetResource = new SpritesheetResource(animation.getSpritesheet()); + spritesheetResource.setKeyframes(keyFrames); + Collection sprites = new ArrayList<>(Collections.singleton(spritesheetResource)); + for (SpritesheetResource info : sprites) { + Resources.spritesheets().getAll().removeIf(x -> x.getName().equals(info.getName() + "-preview")); + this.getGameFile().getSpriteSheets().removeIf(x -> x.getName().equals(info.getName())); + this.getGameFile().getSpriteSheets().add(info); + log.log(Level.INFO, "imported spritesheet {0}", new Object[]{info.getName()}); + } + this.loadSpriteSheets(sprites, true); + + } catch (AsepriteHandler.ImportAnimationException | IOException e) { log.log(Level.SEVERE, e.getMessage(), e); } } diff --git a/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java b/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java index 1531483ae..adfa1173a 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java +++ b/utiliti/src/de/gurkenlabs/utiliti/swing/menus/ResourcesMenu.java @@ -54,8 +54,8 @@ public ResourcesMenu() { exportSpriteSheets.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK)); exportSpriteSheets.addActionListener(a -> Editor.instance().exportSpriteSheets()); - JMenuItem importAnimation = new JMenuItem(Resources.strings().get("menu_assets_importAnimation")); - importAnimation.addActionListener(a -> Editor.instance().importAnimation()); + JMenuItem importAnimation = new JMenuItem(Resources.strings().get("menu_assets_importAnimation")); + importAnimation.addActionListener(a -> Editor.instance().importAnimation()); importAnimation.setEnabled(false); Editor.instance().onLoaded(() -> { @@ -67,7 +67,7 @@ public ResourcesMenu() { importTilesets.setEnabled(Editor.instance().getCurrentResourceFile() != null); importSounds.setEnabled(Editor.instance().getCurrentResourceFile() != null); exportSpriteSheets.setEnabled(Editor.instance().getCurrentResourceFile() != null); - importAnimation.setEnabled(Editor.instance().getCurrentResourceFile() != null); + importAnimation.setEnabled(Editor.instance().getCurrentResourceFile() != null); }); this.add(importSprite); @@ -77,8 +77,8 @@ public ResourcesMenu() { this.add(importBlueprints); this.add(importTilesets); this.add(importSounds); - this.add(importAnimation); - this.addSeparator(); + this.add(importAnimation); + this.addSeparator(); this.add(exportSpriteSheets); this.add(compress); } From c8a0baed97f8a1387b5c53b8932f85afa229e52e Mon Sep 17 00:00:00 2001 From: HannesSundin Date: Wed, 10 Mar 2021 15:11:22 +0100 Subject: [PATCH 15/16] Fix format in AssetPanelItem.exportspriteSheet --- .../utiliti/swing/AssetPanelItem.java | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java index b8d26f867..8ceb6e59b 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java +++ b/utiliti/src/de/gurkenlabs/utiliti/swing/AssetPanelItem.java @@ -445,47 +445,47 @@ private void exportSpritesheet() { chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); chooser.setDialogType(JFileChooser.SAVE_DIALOG); chooser.setDialogTitle("Export Spritesheet"); - switch (answer) { - case 0: { - XmlExportDialog.export(spriteSheetInfo, "Spritesheet", spriteSheetInfo.getName()); - break; - } - case 1: { - FileFilter filter = new FileNameExtensionFilter(format.toString() + " - Image", format.toString()); - chooser.setFileFilter(filter); - chooser.addChoosableFileFilter(filter); - chooser.setSelectedFile(new File(spriteSheetInfo.getName() + format.toFileExtension())); - - int result = chooser.showSaveDialog(Game.window().getRenderComponent()); - if (result == JFileChooser.APPROVE_OPTION) { - ImageSerializer.saveImage(chooser.getSelectedFile().toString(), sprite.getImage(), format); - log.log(Level.INFO, "exported spritesheet {0} to {1}", new Object[]{spriteSheetInfo.getName(), chooser.getSelectedFile()}); - } - break; - } - case 2: { - FileFilter filter = new FileNameExtensionFilter(".json" + " - " + "Spritesheet" + " JSON", "json"); - chooser.setFileFilter(filter); - chooser.addChoosableFileFilter(filter); - chooser.setSelectedFile(new File(spriteSheetInfo.getName() + "." + "json")); - - int result = chooser.showSaveDialog(Game.window().getRenderComponent()); - if (result == JFileChooser.APPROVE_OPTION) { - String fileNameWithExtension = chooser.getSelectedFile().toString(); - if (!fileNameWithExtension.endsWith(".json")) { - fileNameWithExtension += ".json"; - } - String json = AsepriteHandler.exportAnimation(spriteSheetInfo); - try (Writer writer = new FileWriter(fileNameWithExtension)) { - writer.write(json); - log.log(Level.INFO, "Exported {0} {1} to {2}", new Object[]{"Spritesheet", spriteSheetInfo.getName(), fileNameWithExtension}); - } catch (IOException e) { - e.printStackTrace(); - } - } - break; - } - } + switch (answer) { + case 0: { + XmlExportDialog.export(spriteSheetInfo, "Spritesheet", spriteSheetInfo.getName()); + break; + } + case 1: { + FileFilter filter = new FileNameExtensionFilter(format.toString() + " - Image", format.toString()); + chooser.setFileFilter(filter); + chooser.addChoosableFileFilter(filter); + chooser.setSelectedFile(new File(spriteSheetInfo.getName() + format.toFileExtension())); + + int result = chooser.showSaveDialog(Game.window().getRenderComponent()); + if (result == JFileChooser.APPROVE_OPTION) { + ImageSerializer.saveImage(chooser.getSelectedFile().toString(), sprite.getImage(), format); + log.log(Level.INFO, "exported spritesheet {0} to {1}", new Object[]{spriteSheetInfo.getName(), chooser.getSelectedFile()}); + } + break; + } + case 2: { + FileFilter filter = new FileNameExtensionFilter(".json" + " - " + "Spritesheet" + " JSON", "json"); + chooser.setFileFilter(filter); + chooser.addChoosableFileFilter(filter); + chooser.setSelectedFile(new File(spriteSheetInfo.getName() + "." + "json")); + + int result = chooser.showSaveDialog(Game.window().getRenderComponent()); + if (result == JFileChooser.APPROVE_OPTION) { + String fileNameWithExtension = chooser.getSelectedFile().toString(); + if (!fileNameWithExtension.endsWith(".json")) { + fileNameWithExtension += ".json"; + } + String json = AsepriteHandler.exportAnimation(spriteSheetInfo); + try (Writer writer = new FileWriter(fileNameWithExtension)) { + writer.write(json); + log.log(Level.INFO, "Exported {0} {1} to {2}", new Object[]{"Spritesheet", spriteSheetInfo.getName(), fileNameWithExtension}); + } catch (IOException e) { + e.printStackTrace(); + } + } + break; + } + } } catch (AsepriteHandler.ExportAnimationException | IOException e) { log.log(Level.SEVERE, e.getMessage(), e); } From ad99750231f32f84d30aac2bfd640ba6ff9b3342 Mon Sep 17 00:00:00 2001 From: Daniel Halvarsson Date: Wed, 10 Mar 2021 15:48:49 +0100 Subject: [PATCH 16/16] Fix formatting --- utiliti/src/de/gurkenlabs/utiliti/components/Editor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java index 47442d83d..d5c014810 100644 --- a/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java +++ b/utiliti/src/de/gurkenlabs/utiliti/components/Editor.java @@ -79,7 +79,7 @@ public class Editor extends Screen { private static final String TEXTUREATLAS_FILE_NAME = "Texture Atlas XML (generic)"; private static final String IMPORT_DIALOGUE = "import_something"; - private static final String ANIMATION_FILE_NAME = "Animation file"; + private static final String ANIMATION_FILE_NAME = "Animation file"; private static Editor instance; private static UserPreferences preferences;