Issue #895 Correct handling CMYK JPEG images (Monte Video library)

Correct JPEG to Image conversion when it has alpha
This commit is contained in:
Jindra Petřík
2015-05-26 20:01:22 +02:00
parent a54392e3f5
commit 8074aa5fd8
20 changed files with 3641 additions and 120 deletions

View File

@@ -1,108 +1,111 @@
/*
* Copyright (C) 2010-2015 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.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings;
import com.jpexs.decompiler.flash.helpers.BMPFile;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class ImageExporter {
public List<File> exportImages(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, ImageExportSettings settings, EventListener evl) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
Path.createDirectorySafe(foutdir);
int count = 0;
for (Tag t : tags) {
if (t instanceof ImageTag) {
count++;
}
}
if (count == 0) {
return ret;
}
int currentIndex = 1;
for (Tag t : tags) {
if (t instanceof ImageTag) {
if (evl != null) {
evl.handleExportingEvent("image", currentIndex, count, t.getName());
}
final ImageTag imageTag = (ImageTag) t;
ImageFormat fileFormat = imageTag.getImageFormat();
if (settings.mode == ImageExportMode.PNG) {
fileFormat = ImageFormat.PNG;
}
if (settings.mode == ImageExportMode.JPEG) {
fileFormat = ImageFormat.JPEG;
}
if (settings.mode == ImageExportMode.BMP) {
fileFormat = ImageFormat.BMP;
}
{
final File file = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + "." + ImageHelper.getImageFormatString(fileFormat)));
final ImageFormat ffileFormat = fileFormat;
new RetryTask(() -> {
if (ffileFormat == ImageFormat.BMP) {
BMPFile.saveBitmap(imageTag.getImage().getBufferedImage(), file);
} else {
ImageHelper.write(imageTag.getImage().getBufferedImage(), ffileFormat, file);
}
}, handler).run();
ret.add(file);
}
if (evl != null) {
evl.handleExportedEvent("image", currentIndex, count, t.getName());
}
currentIndex++;
}
}
return ret;
}
}
/*
* Copyright (C) 2010-2015 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.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings;
import com.jpexs.decompiler.flash.helpers.BMPFile;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class ImageExporter {
public List<File> exportImages(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, ImageExportSettings settings, EventListener evl) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
Path.createDirectorySafe(foutdir);
int count = 0;
for (Tag t : tags) {
if (t instanceof ImageTag) {
count++;
}
}
if (count == 0) {
return ret;
}
int currentIndex = 1;
for (Tag t : tags) {
if (t instanceof ImageTag) {
if (evl != null) {
evl.handleExportingEvent("image", currentIndex, count, t.getName());
}
final ImageTag imageTag = (ImageTag) t;
ImageFormat fileFormat = imageTag.getImageFormat();
ImageFormat originalFormat = fileFormat;
if (settings.mode == ImageExportMode.PNG) {
fileFormat = ImageFormat.PNG;
}
if (settings.mode == ImageExportMode.JPEG) {
fileFormat = ImageFormat.JPEG;
}
if (settings.mode == ImageExportMode.BMP) {
fileFormat = ImageFormat.BMP;
}
{
final File file = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + "." + ImageHelper.getImageFormatString(fileFormat)));
final ImageFormat ffileFormat = fileFormat;
new RetryTask(() -> {
if (ffileFormat == originalFormat) {
} else if (ffileFormat == ImageFormat.BMP) {
BMPFile.saveBitmap(imageTag.getImage().getBufferedImage(), file);
} else {
ImageHelper.write(imageTag.getImage().getBufferedImage(), ffileFormat, file);
}
}, handler).run();
ret.add(file);
}
if (evl != null) {
evl.handleExportedEvent("image", currentIndex, count, t.getName());
}
currentIndex++;
}
}
return ret;
}
}

View File

@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.helpers;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.helpers.Helper;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -25,10 +26,15 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.monte.media.jpeg.CMYKJPEGImageReader;
import org.monte.media.jpeg.CMYKJPEGImageReaderSpi;
/**
*
@@ -41,9 +47,18 @@ public class ImageHelper {
}
public static BufferedImage read(InputStream input) throws IOException {
BufferedImage in = ImageIO.read(input);
if (in == null) {
return null;
BufferedImage in;
byte data[] = Helper.readStream(input);
try (ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(data))) {
CMYKJPEGImageReader r = new CMYKJPEGImageReader(new CMYKJPEGImageReaderSpi());
r.setInput(iis);
in = r.read(0);
} catch (IOException ex) {
try {
return ImageIO.read(ImageIO.createImageInputStream(new ByteArrayInputStream(data)));
} catch (IOException ex1) {
in = null;
}
}
int type = in.getType();

View File

@@ -58,7 +58,7 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag {
public DefineBitsJPEG2Tag(SWF swf) {
super(swf, ID, NAME, null);
characterID = swf.getNextCharacterId();
imageData = ByteArrayRange.EMPTY;
imageData = new ByteArrayRange(createEmptyImage());
forceWriteAsLong = true;
}
@@ -68,6 +68,13 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag {
this.imageData = new ByteArrayRange(imageData);
}
private byte[] createEmptyImage() {
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
ByteArrayOutputStream bitmapDataOS = new ByteArrayOutputStream();
ImageHelper.write(img, ImageFormat.JPEG, bitmapDataOS);
return bitmapDataOS.toByteArray();
}
public DefineBitsJPEG2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException {
super(sis.getSwf(), ID, NAME, data);
readData(sis, data, 0, false, false, false);

View File

@@ -153,7 +153,7 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag {
@Override
public ImageFormat getImageFormat() {
ImageFormat fmt = ImageTag.getImageFormat(imageData);
if (fmt == ImageFormat.JPEG) {
if (fmt == ImageFormat.JPEG && bitmapAlphaData.getLength() > 0) {
fmt = ImageFormat.PNG; //transparency
}
return fmt;
@@ -161,8 +161,17 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag {
@Override
public InputStream getImageData() {
int errorLength = hasErrorHeader(imageData) ? 4 : 0;
return new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength);
if (bitmapAlphaData.getLength() == 0) { //No alpha, then its JPEG
int errorLength = hasErrorHeader(imageData) ? 4 : 0;
return new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength);
}
//Make PNG
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageHelper.write(getImage().getBufferedImage(), ImageFormat.PNG, baos);
return new ByteArrayInputStream(baos.toByteArray());
}
@Override
@@ -171,7 +180,10 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag {
return cachedImage;
}
try {
BufferedImage image = ImageHelper.read(getImageData());
int errorLength = hasErrorHeader(imageData) ? 4 : 0;
ByteArrayInputStream bis = new ByteArrayInputStream(imageData.getArray(), imageData.getPos() + errorLength, imageData.getLength() - errorLength);
BufferedImage image = ImageHelper.read(bis);
if (image == null) {
Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, "Failed to load image");
return null;

View File

@@ -158,7 +158,7 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag {
@Override
public ImageFormat getImageFormat() {
ImageFormat fmt = ImageTag.getImageFormat(imageData);
if (fmt == ImageFormat.JPEG) {
if (fmt == ImageFormat.JPEG && bitmapAlphaData.getLength() > 0) {
fmt = ImageFormat.PNG; //transparency
}
return fmt;
@@ -166,7 +166,15 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag {
@Override
public InputStream getImageData() {
return new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength());
if (bitmapAlphaData.getLength() == 0) { //No alpha, then its JPEG
return new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength());
}
//Make PNG
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageHelper.write(getImage().getBufferedImage(), ImageFormat.PNG, baos);
return new ByteArrayInputStream(baos.toByteArray());
}
@Override
@@ -175,7 +183,8 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag {
return cachedImage;
}
try {
BufferedImage image = ImageHelper.read(getImageData());
BufferedImage image = ImageHelper.read(new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()));
if (image == null) {
Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to load image");
return null;