diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b88eb690..ffb9c97bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. ### Added - PR119 Option to set scale factor in advanced settings (Set it to 2.0 on Mac retina displays) +### Fixed +- [#1880] JPEG Fixer + ## [17.0.0] - 2022-11-20 ### Added - [#1870] AS3 Adding new class - Target DoABC tag or position can be selected to prevent Error 1014 @@ -2497,7 +2500,8 @@ All notable changes to this project will be documented in this file. ### Added - Initial public release -[Unreleased]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version16.3.1...dev +[Unreleased]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version17.0.0...dev +[17.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version16.3.1...version17.0.0 [16.3.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version16.3.0...version16.3.1 [16.3.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version16.2.0...version16.3.0 [16.2.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version16.1.0...version16.2.0 @@ -2627,6 +2631,7 @@ All notable changes to this project will be documented in this file. [alpha 9]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha8...alpha9 [alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 +[#1880]: https://www.free-decompiler.com/flash/issues/1880 [#1870]: https://www.free-decompiler.com/flash/issues/1870 [#1871]: https://www.free-decompiler.com/flash/issues/1871 [#1875]: https://www.free-decompiler.com/flash/issues/1875 diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegAnalyzer.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegAnalyzer.java new file mode 100644 index 000000000..1e870871a --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegAnalyzer.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.helpers; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author JPEXS + */ +public class JpegAnalyzer { + + + + public void analyze(InputStream is) throws IOException { + int val; + + while (true) { + val = is.read(); + if (val == -1) { + break; + } + if (val == 0xFF) { + val = is.read(); + if (val == -1) { + break; + } + if (val != 0) { + int len = 2; + if (JpegMarker.markerHasLength(val)) { + int len1 = is.read(); + int len2 = is.read(); + len = (len1 << 8) + len2; + is.skip(len - 2); + } + System.out.println("marker " + JpegMarker.markerToString(val) + " len: " + len); + } + } + } + } + + + + public static void main(String[] args) throws FileNotFoundException, IOException { + JpegAnalyzer analyzer = new JpegAnalyzer(); + analyzer.analyze(new FileInputStream("sample.jpg")); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java index 2053f5dd5..d60d2812a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java @@ -109,9 +109,9 @@ public class JpegFixer { } //main removing EOI+SOI - while ((val = is.read()) > -1) { + loopread: while ((val = is.read()) > -1) { if (val == 0xFF) { - val = is.read(); + val = is.read(); if (val == 0) { os.write(0xFF); os.write(val); @@ -127,10 +127,36 @@ public class JpegFixer { os.write(0xFF); os.write(EOI); os.write(0xFF); - os.write(val); + if (val != -1) { + os.write(val); + } } else if (val != EOI) { os.write(0xFF); - os.write(val); + if (val != -1) { + os.write(val); + } + } + + if (val != -1 && JpegMarker.markerHasLength(val)) { + int len1 = is.read(); + if (len1 == -1) { + break; + } + int len2 = is.read(); + if (len2 == -1) { + os.write(len1); + break; + } + os.write(len1); + os.write(len2); + int len = (len1 << 8) + len2; + for (int i = 0; i < len - 2; i++) { + int val2 = is.read(); + if (val2 == -1) { + break loopread; + } + os.write(val2); + } } prevEoi = val == EOI; diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegMarker.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegMarker.java new file mode 100644 index 000000000..05ad24caa --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegMarker.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010-2022 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.helpers; + +import java.lang.reflect.Field; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class JpegMarker { + public static final int SOF0 = 0xC0; //Start of Frame 0 + public static final int SOF1 = 0xC1; //Start of Frame 1 + public static final int SOF2 = 0xC2; //Start of Frame 2 + public static final int SOF3 = 0xC3; //Start of Frame 3 + + public static final int DHT = 0xC4; //Define Huffman Table + + public static final int SOF5 = 0xC5; //Start of Frame 5 + public static final int SOF6 = 0xC6; //Start of Frame 6 + public static final int SOF7 = 0xC7; //Start of Frame 7 + + public static final int JPG = 0xC8; //JPEG Extensions + + public static final int SOF9 = 0xC9; //Start of Frame 9 + public static final int SOF10 = 0xCA; //Start of Frame 10 + public static final int SOF11 = 0xCB; //Start of Frame 11 + + public static final int DAC = 0xCC; //Define Arithmetic Coding + + public static final int SOF13 = 0xCD; //Start of Frame 13 + public static final int SOF14 = 0xCE; //Start of Frame 14 + public static final int SOF15 = 0xCF; //Start of Frame 15 + + public static final int RST0 = 0xD0; //Restart Marker 0 + public static final int RST1 = 0xD1; //Restart Marker 1 + public static final int RST2 = 0xD2; //Restart Marker 2 + public static final int RST3 = 0xD3; //Restart Marker 3 + public static final int RST4 = 0xD4; //Restart Marker 4 + public static final int RST5 = 0xD5; //Restart Marker 5 + public static final int RST6 = 0xD6; //Restart Marker 6 + public static final int RST7 = 0xD7; //Restart Marker 7 + + public static final int SOI = 0xD8; //Start of Image + public static final int EOI = 0xD9; //End of Image + + public static final int SOS = 0xDA; //Start of Scan + public static final int DQT = 0xDB; //Define Quantization Table + public static final int DNL = 0xDC; //Define Number of Lines + public static final int DRI = 0xDD; //Define Restart Interval + public static final int DHP = 0xDE; //Define Hierarchical Progression + public static final int EXP = 0xDF; //Expand Reference Component + + public static final int APP0 = 0xE0; //Application Segment 0 + public static final int APP1 = 0xE1; //Application Segment 1 + public static final int APP2 = 0xE2; //Application Segment 2 + public static final int APP3 = 0xE3; //Application Segment 3 + public static final int APP4 = 0xE4; //Application Segment 4 + public static final int APP5 = 0xE5; //Application Segment 5 + public static final int APP6 = 0xE6; //Application Segment 6 + public static final int APP7 = 0xE7; //Application Segment 7 + public static final int APP8 = 0xE8; //Application Segment 8 + public static final int APP9 = 0xE9; //Application Segment 9 + public static final int APP10 = 0xEA; //Application Segment 10 + public static final int APP11 = 0xEB; //Application Segment 11 + public static final int APP12 = 0xEC; //Application Segment 12 + public static final int APP13 = 0xED; //Application Segment 13 + public static final int APP14 = 0xEE; //Application Segment 14 + public static final int APP15 = 0xEF; //Application Segment 15 + + public static final int JPG0 = 0xF0; //JPEG Extension 0 + public static final int JPG1 = 0xF1; //JPEG Extension 1 + public static final int JPG2 = 0xF2; //JPEG Extension 2 + public static final int JPG3 = 0xF3; //JPEG Extension 3 + public static final int JPG4 = 0xF4; //JPEG Extension 4 + public static final int JPG5 = 0xF5; //JPEG Extension 5 + public static final int JPG6 = 0xF6; //JPEG Extension 6 + public static final int JPG7 = 0xF7; //JPEG Extension 7 + public static final int SOF48 = 0xF7; //JPEG-LS + public static final int JPG8 = 0xF8; //JPEG Extension 8 + public static final int LSE = 0xF8; //JPEG Extension 8 + public static final int JPG9 = 0xF9; //JPEG Extension 9 + public static final int JPG10 = 0xFA; //JPEG Extension 10 + public static final int JPG11 = 0xFB; //JPEG Extension 11 + public static final int JPG12 = 0xFC; //JPEG Extension 12 + public static final int JPG13 = 0xFD; //JPEG Extension 13 + public static final int COM = 0xFE; //Comment + + public static boolean markerHasLength(int marker) { + return marker != 0 + && marker != SOI + && marker != EOI + && marker != RST0 + && marker != RST1 + && marker != RST2 + && marker != RST3 + && marker != RST4 + && marker != RST5 + && marker != RST6 + && marker != RST7; + } + + public static String markerToString(int marker) { + for (Field field : JpegAnalyzer.class.getDeclaredFields()) { + try { + if (field.getInt(null) == marker) { + return field.getName(); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + Logger.getLogger(JpegMarker.class.getName()).log(Level.SEVERE, null, ex); + } + } + return "0x" + Integer.toHexString(marker); + } +} diff --git a/libsrc/ffdec_lib/test/com/jpexs/helpers/JpegFixerTest.java b/libsrc/ffdec_lib/test/com/jpexs/helpers/JpegFixerTest.java index 94d9b5e46..903231c01 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/helpers/JpegFixerTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/helpers/JpegFixerTest.java @@ -37,40 +37,84 @@ public class JpegFixerTest { public static Object[][] provideSamples() { return new Object[][]{ { - new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, (byte) 0xFF, (byte) SOI, (byte) 0x21, (byte) 0xFF, (byte) EOI}, - new byte[]{(byte) 0xFF, (byte) SOI, (byte) 0x21, (byte) 0xFF, (byte) EOI},}, + new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x21, + (byte) 0xFF, (byte) EOI}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x21, + (byte) 0xFF, (byte) EOI},}, { new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI}, new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI},}, { - new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, 0x23}, - new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, 0x23},}, + new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x23 + }, + new byte[]{(byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x23 + },}, { new byte[]{(byte) 0xFF, (byte) EOI}, - new byte[]{(byte) 0xFF, (byte) EOI},}, + new byte[]{(byte) 0xFF, (byte) EOI}}, { new byte[]{(byte) 0xFF}, - new byte[]{(byte) 0xFF},}, + new byte[]{(byte) 0xFF} + }, { new byte[]{(byte) 0x26}, - new byte[]{(byte) 0x26},}, + new byte[]{(byte) 0x26} + }, + { + new byte[]{(byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x27}, + new byte[]{(byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x27} + }, { new byte[]{}, - new byte[]{},}, + new byte[]{}}, { - new byte[]{(byte) 0xFF, (byte) SOI, 0x27, 0x37, 0x47, 0x57, (byte) 0xFF, (byte) EOI}, - new byte[]{(byte) 0xFF, (byte) SOI, 0x27, 0x37, 0x47, 0x57, (byte) 0xFF, (byte) EOI} + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x29, + (byte) 0xFF, (byte) EOI}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x29, + (byte) 0xFF, (byte) EOI} }, { - new byte[]{(byte) 0xFF, (byte) SOI, 0x28, 0x38, (byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, 0x48, 0x58, (byte) 0xFF, (byte) EOI}, - new byte[]{(byte) 0xFF, (byte) SOI, 0x28, 0x38, 0x48, 0x58, (byte) 0xFF, (byte) EOI} + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x2A, + (byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x3A, + (byte) 0xFF, (byte) EOI}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x2A, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x3A, + (byte) 0xFF, (byte) EOI} }, { - new byte[]{(byte) 0xFF, (byte) SOI, 0x29, (byte) 0xFF, (byte) SOI, 0x39, (byte) 0xFF, (byte) EOI}, - new byte[]{(byte) 0xFF, (byte) SOI, 0x29, 0x39, (byte) 0xFF, (byte) EOI},}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x2B, + (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x3B, + (byte) 0xFF, (byte) EOI}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x2B, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x3B, + (byte) 0xFF, (byte) EOI},}, { - new byte[]{(byte) 0xFF, (byte) SOI, (byte) 0xFF, (byte) SOI, 0x2A, (byte) 0xFF, (byte) EOI}, - new byte[]{(byte) 0xFF, (byte) SOI, 0x2A, (byte) 0xFF, (byte) EOI} + new byte[]{(byte) 0xFF, (byte) SOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x2C, + (byte) 0xFF, (byte) EOI}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x03, 0x2C, + (byte) 0xFF, (byte) EOI} + }, + { + new byte[]{(byte) 0xFF, (byte) SOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x06, (byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) EOI}, + new byte[]{(byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) JpegMarker.APP0, 0x00, 0x06, (byte) 0xFF, (byte) EOI, (byte) 0xFF, (byte) SOI, + (byte) 0xFF, (byte) EOI} } }; } diff --git a/out_after.jpg b/out_after.jpg new file mode 100644 index 000000000..b433b279a Binary files /dev/null and b/out_after.jpg differ diff --git a/out_before.jpg b/out_before.jpg new file mode 100644 index 000000000..e01345757 Binary files /dev/null and b/out_before.jpg differ