diff --git a/CHANGELOG.md b/CHANGELOG.md index d8a9202b7..0610abba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Export dialog - Icons per export type and arrow symbol - Updated German translation (AI used) - Slovak translation (AI used) +- APNG (animated PNG) export for frames, sprites and morphshapes ### Fixed - [#2603] Export Sprite as SWF - sprite frames as timeline frames, without sprite itself diff --git a/lib/jpexs-apng.jar b/lib/jpexs-apng.jar new file mode 100644 index 000000000..603c78099 Binary files /dev/null and b/lib/jpexs-apng.jar differ diff --git a/lib/jpexs-apng.license.txt b/lib/jpexs-apng.license.txt new file mode 100644 index 000000000..d7a897259 --- /dev/null +++ b/lib/jpexs-apng.license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Jindra Petřík + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libsrc/ffdec_lib/README.md b/libsrc/ffdec_lib/README.md index d2a2c74bf..875566d7b 100644 --- a/libsrc/ffdec_lib/README.md +++ b/libsrc/ffdec_lib/README.md @@ -34,6 +34,7 @@ These include: * FLA Compound Document Tools - `flacomdoc.jar` - Exporting FLA to CS4 or lower * TomlJ - `tomlj-1.1.1.jar` - Storing configuration * ANTLR - `antlr-runtime-4.11.1.jar` - Storing configuration +* jpexs-apng - `jpexs-apng.jar` - APNG export ## Basic library usage ```java @@ -145,6 +146,7 @@ And also links to these libraries: * [FLA Compound Document Tools] (Exporting to FLA CS4 and below) - LGPLv2.1 * [TomlJ] (Storing configuration) - Apache License 2.0 * [ANTLR] (Storing configuration) - BSD 3-Clause +* [jpexs-apng] (Exporting APNG) - MIT License [sfntly]: https://code.google.com/p/sfntly/ [JLayer]: http://www.javazoom.net/javalayer/javalayer.html @@ -167,4 +169,5 @@ And also links to these libraries: [FLA Compound Document Tools]: https://github.com/jindrapetrik/flacomdoc [TomlJ]: https://github.com/tomlj/tomlj [ANTLR]: https://www.antlr.org/ -[webp4j]: https://github.com/jindrapetrik/webp4j/tree/java8 \ No newline at end of file +[webp4j]: https://github.com/jindrapetrik/webp4j/tree/java8 +[jpexs-apng]: https://github.com/jindrapetrik/jpexs-apng \ No newline at end of file diff --git a/libsrc/ffdec_lib/lib/jpexs-apng.jar b/libsrc/ffdec_lib/lib/jpexs-apng.jar new file mode 100644 index 000000000..603c78099 Binary files /dev/null and b/libsrc/ffdec_lib/lib/jpexs-apng.jar differ diff --git a/libsrc/ffdec_lib/lib/jpexs-apng.license.txt b/libsrc/ffdec_lib/lib/jpexs-apng.license.txt new file mode 100644 index 000000000..d7a897259 --- /dev/null +++ b/libsrc/ffdec_lib/lib/jpexs-apng.license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Jindra Petřík + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libsrc/ffdec_lib/nbproject/project.xml b/libsrc/ffdec_lib/nbproject/project.xml index 5c7c8f075..6983a635a 100644 --- a/libsrc/ffdec_lib/nbproject/project.xml +++ b/libsrc/ffdec_lib/nbproject/project.xml @@ -242,7 +242,7 @@ auxiliary.show.customizer.message= src - ../../src;lib/LZMA.jar;lib/avi.jar;lib/cmykjpeg.jar;lib/ddsreader.jar;lib/gif.jar;lib/gnujpdf.jar;lib/jlayer-1.0.2.jar;lib/jpacker.jar;lib/nellymoser.jar;lib/sfntly.jar;lib/tga.jar;lib/ttf.jar;lib/vlcj-4.7.3.jar;lib/vlcj-natives-4.7.0.jar;lib/flashdebugger.jar;lib/jna-3.5.1.jar;lib/jna-platform-3.5.1.jar;lib/gifreader.jar;lib/miterstroke.jar;lib/decimal.jar;lib/flacomdoc.jar;lib/tomlj-1.1.1.jar;lib/decimal.jar;lib/webp4j-1.4.0.jar + ../../src;lib/LZMA.jar;lib/avi.jar;lib/cmykjpeg.jar;lib/ddsreader.jar;lib/gif.jar;lib/gnujpdf.jar;lib/jlayer-1.0.2.jar;lib/jpacker.jar;lib/nellymoser.jar;lib/sfntly.jar;lib/tga.jar;lib/ttf.jar;lib/vlcj-4.7.3.jar;lib/vlcj-natives-4.7.0.jar;lib/flashdebugger.jar;lib/jna-3.5.1.jar;lib/jna-platform-3.5.1.jar;lib/gifreader.jar;lib/miterstroke.jar;lib/decimal.jar;lib/flacomdoc.jar;lib/tomlj-1.1.1.jar;lib/decimal.jar;lib/webp4j-1.4.0.jar;lib/jpexs-apng.jar build reports dist diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index f40b6c7f5..5b67618a8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -65,6 +65,10 @@ import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; import com.jpexs.helpers.SerializableImage; import com.jpexs.helpers.utf8.Utf8Helper; +import com.jpexs.images.apng.AnimatedPngDecoder; +import com.jpexs.images.apng.AnimatedPngEncoder; +import com.jpexs.images.apng.data.AnimatedPngData; +import com.jpexs.images.apng.data.AnimationFrameData; import dev.matrixlab.webp4j.WebPCodec; import dev.matrixlab.webp4j.animation.AnimatedWebPEncoder; import dev.matrixlab.webp4j.gif.GifToWebPConfig; @@ -151,6 +155,9 @@ public class FrameExporter { case PNG: fem = FrameExportMode.PNG; break; + case APNG: + fem = FrameExportMode.APNG; + break; case GIF: fem = FrameExportMode.GIF; break; @@ -563,6 +570,16 @@ public class FrameExporter { }, handler).run(); } break; + case APNG: + for (File foutdir : foutdirs) { + frameImages.reset(); + new RetryTask(() -> { + File f = new File(foutdir + File.separator + "frames.png"); + makeAnimatedPng(frameImages, swf.frameRate, f, evl); + ret.add(f); + }, handler).run(); + } + break; case BMP: for (File foutdir : foutdirs) { frameImages.reset(); @@ -831,6 +848,33 @@ public class FrameExporter { encoder.finish(); } + public static void makeAnimatedPng(Iterator images, float frameRate, File file, EventListener evl) throws IOException { + + List frames = new ArrayList<>(); + + int delayNumerator = 1000; + int delayDenominator = Math.round(1000 * frameRate); + int width = 0; + int height = 0; + BufferedImage backupImage = null; + while (images.hasNext()) { + BufferedImage img = images.next(); + if (img == null) { + break; + } + if (backupImage == null) { + backupImage = img; + } + width = img.getWidth(); + height = img.getHeight(); + frames.add(new AnimationFrameData(img, delayNumerator, delayDenominator)); + } + AnimatedPngData data = new AnimatedPngData(width, height, 0, backupImage, frames); + try (FileOutputStream fos = new FileOutputStream(file)) { + AnimatedPngEncoder.encode(data, fos); + } + } + public static void makeAnimatedWebP(Iterator images, float frameRate, File file, EventListener evl) throws IOException { GifToWebPConfig config = GifToWebPConfig.createLosslessConfig(); List allImages = new ArrayList<>(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java index 938d1e1b3..bdcafd531 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java @@ -349,6 +349,13 @@ public class MorphShapeExporter { ret.add(file); }, handler).run(); break; + case APNG: + frameImages.reset(); + new RetryTask(() -> { + FrameExporter.makeAnimatedPng(frameImages, PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE, file, evl); + ret.add(file); + }, handler).run(); + break; case PNG_FRAMES: case BMP_FRAMES: case WEBP_FRAMES: diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java index 4f4dc8c5a..a56de324c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java @@ -28,6 +28,10 @@ public enum FrameExportMode { * PNG - Portable Network Graphics */ PNG, + /** + * APNG - Animated Portable Network Graphics + */ + APNG, /** * GIF - Graphics Interchange Format */ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java index f23fa7f98..0485b5b05 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java @@ -48,6 +48,10 @@ public enum MorphShapeExportMode { * PNG individual frames - Portable Network Graphics */ PNG_FRAMES(false, true), + /** + * APNG - Animated Portable Network Graphics + */ + APNG(true, false), /** * BMP start and end frames - Windows Bitmap */ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java index 7f98526b7..eb66b812a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java @@ -28,6 +28,10 @@ public enum SpriteExportMode { * PNG - Portable Network Graphics */ PNG, + /** + * APNG - Animated Portable Network Graphics + */ + APNG, /** * GIF - Graphics Interchange Format */ diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java index 147ea15ae..961dea5bb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java @@ -95,6 +95,7 @@ public class MorphShapeExportSettings { switch (mode) { case PNG_START_END: case PNG_FRAMES: + case APNG: return ".png"; case BMP_START_END: case BMP_FRAMES: diff --git a/src/com/jpexs/decompiler/flash/console/help.txt b/src/com/jpexs/decompiler/flash/console/help.txt index 8a9b97dfd..7f0a2b8ec 100644 --- a/src/com/jpexs/decompiler/flash/console/help.txt +++ b/src/com/jpexs/decompiler/flash/console/help.txt @@ -333,7 +333,9 @@ Pre-options: morphshape:bmp_frames - BMP frames format for MorphShapes morphshape:webp_frames - WEBP frames format for MorphShapes morphshape:webp - WEBP format for MorphShapes + morphshape:apng - APNG format for MorphShapes frame:png - PNG format for Frames + frame:apng - APNG format for Frames frame:gif - GIF format for Frames frame:avi - AVI format for Frames frame:svg - SVG format for Frames @@ -343,6 +345,7 @@ Pre-options: frame:webp - WEBP format for Frames frame:webp_animated - WEBP animated format for Frames sprite:png - PNG format for Sprites + sprite:apng - APNG format for Sprites sprite:gif - GIF format for Sprites sprite:avi - AVI format for Sprites sprite:svg - SVG format for Sprites diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index b98f4b735..ce130a70d 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -117,4 +117,7 @@ morph.duration.seconds = s morph.numberOfFrames = Number of morph frames morph.duration.invalid = Invalid morph duration. Enter positive float value in seconds. morph.numberOfFrames.invalid = Invalid number of frames. Enter positive integer value >= 2. -arrow = \ud83e\udc06 \ No newline at end of file +arrow = \ud83e\udc06 +frames.apng = APNG +sprites.apng = APNG +morphshapes.apng = APNG \ No newline at end of file