From 94176f15a65a0af23f9b872213dc36d2b2d566d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Wed, 24 Feb 2021 19:39:00 +0100 Subject: [PATCH] Fixed: #1153, #1347, #1552, #1553 Images export for some nonstandard JPEGs --- CHANGELOG.md | 1 + .../flash/tags/DefineBitsJPEG2Tag.java | 14 +- .../flash/tags/DefineBitsJPEG3Tag.java | 13 +- .../flash/tags/DefineBitsJPEG4Tag.java | 14 +- .../src/com/jpexs/helpers/JpegFixer.java | 129 ++++++++++++++++++ 5 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b8fc4b633..aff699be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ All notable changes to this project will be documented in this file. - AS1/2 Goto search result not properly selecting line (delay) - ActiveX exceptions when FlashPlayer disabled in classic GUI - #1569 AS3 direct editation - incorrect slot names handling (IndexOutOfBounds) +- #1153, #1347, #1552, #1553 Images export for some nonstandard JPEGs ### Changed - #1565, #1407, #1350 On BinaryData SWF save, parent SWF is saved diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java index 39210ee6c..115837033 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; @@ -26,6 +27,7 @@ import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.JpegFixer; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; import java.awt.image.BufferedImage; @@ -125,7 +127,15 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag { @Override public InputStream getOriginalImageData() { int errorLength = hasErrorHeader(imageData) ? 4 : 0; - return new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength); + JpegFixer jpegFixer = new JpegFixer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + jpegFixer.fixJpeg(new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength), baos); + } catch (IOException ex) { + Logger.getLogger(DefineBitsJPEG2Tag.class.getName()).log(Level.SEVERE, null, ex); + } + + return new ByteArrayInputStream(baos.toByteArray()); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java index c785aac18..d0ac3db6a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; @@ -27,6 +28,7 @@ import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.JpegFixer; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; import java.awt.image.BufferedImage; @@ -174,7 +176,14 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { public InputStream getOriginalImageData() { if (bitmapAlphaData.getLength() == 0) { // No alpha int errorLength = hasErrorHeader(imageData) ? 4 : 0; - return new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength); + JpegFixer jpegFixer = new JpegFixer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + jpegFixer.fixJpeg(new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength), baos); + } catch (IOException ex) { + Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, null, ex); + } + return new ByteArrayInputStream(baos.toByteArray()); } return null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java index 2f3fa918f..c4ddb2b99 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; @@ -27,6 +28,7 @@ import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.JpegFixer; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; import java.awt.image.BufferedImage; @@ -178,7 +180,15 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { @Override public InputStream getOriginalImageData() { if (bitmapAlphaData.getLength() == 0) { // No alpha - return new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()); + + JpegFixer jpegFixer = new JpegFixer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + jpegFixer.fixJpeg(new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()), baos); + } catch (IOException ex) { + Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, null, ex); + } + return new ByteArrayInputStream(baos.toByteArray()); } return null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java new file mode 100644 index 000000000..36feaf102 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/JpegFixer.java @@ -0,0 +1,129 @@ +package com.jpexs.helpers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Fixes probles in some JPEGs to be readable by standard viewers. + * + * @author JPEXS + */ +public class JpegFixer { + + private static final int SOI = 0xD8; + private static final int EOI = 0xD9; + private static final int SOF0 = 0xC0; + private static final int SOF1 = 0xC1; + private static final int SOF2 = 0xC2; + private static final int SOF3 = 0xC3; + private static final int SOF5 = 0xC5; + private static final int SOF6 = 0xC6; + private static final int SOF7 = 0xC7; + private static final int SOF9 = 0xC9; + private static final int SOF10 = 0xCA; + private static final int SOF11 = 0xCB; + private static final int SOF13 = 0xCD; + private static final int SOF14 = 0xCE; + private static final int SOF15 = 0xCF; + + private static final int APP0 = 0xE0; + + + public void fixJpeg(InputStream is, OutputStream os) throws IOException { + List data = new ArrayList<>(); + List markers = new ArrayList<>(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int lastMarker = -1; + int val = is.read(); + if (val == -1) { + return; + } + if (val == 0xFF) { + val = is.read(); + if (val != SOI) { + //not a JPEG file, proceed as is + os.write(0xFF); + os.write(val); + while ((val = is.read()) > -1) { + os.write(val); + } + return; + } + } else { + //not a JPEG file, proceed as is + os.write(val); + while ((val = is.read()) > -1) { + os.write(val); + } + return; + } + while ((val = is.read()) > -1) { + if (val == 0xFF) { + val = is.read(); + if (val == 0) { + baos.write(0xff); + baos.write(val); + continue; + } + } else { + baos.write(val); + continue; + } + if (lastMarker > -1) { + data.add(baos.toByteArray()); + markers.add(lastMarker); + baos = new ByteArrayOutputStream(); + } + lastMarker = val; + } + if (lastMarker > -1) { + data.add(baos.toByteArray()); + markers.add(lastMarker); + } + + boolean wasApp0 = false; + for (int i = 0; i < data.size(); i++) { + if (markers.get(i) == APP0) { + wasApp0 = true; + } + if (i > 0 && markers.get(i) == SOI && markers.get(i - 1) == EOI && !wasApp0) { + markers.remove(i); + data.remove(i); + markers.remove(i - 1); + data.remove(i - 1); + i--; + List dataToMove = new ArrayList<>(); + List markersToMove = new ArrayList<>(); + for (int j = i; j < data.size(); j++) { + //move these data up + if (markers.get(j) == APP0 || Arrays.asList(SOF0, SOF1, SOF2, SOF3, SOF5, SOF6, SOF7, SOF9, SOF10, SOF11, SOF13, SOF14, SOF15).contains(markers.get(j))) { + markersToMove.add(markers.get(j)); + dataToMove.add(data.get(j)); + data.remove(j); + markers.remove(j); + j--; + } else { + break; + } + } + data.addAll(1, dataToMove); + markers.addAll(1, markersToMove); + break; + } + } + + os.write(0xFF); + os.write(SOI); + for (int i = 0; i < data.size(); i++) { + os.write(0xFF); + os.write(markers.get(i)); + os.write(data.get(i)); + } + } +}