diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/ImageBinDataGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/ImageBinDataGenerator.java index 2a09a9618..fbcc8ef87 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/ImageBinDataGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/ImageBinDataGenerator.java @@ -21,8 +21,6 @@ import com.jpexs.helpers.Helper; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -31,11 +29,18 @@ import java.util.zip.DeflaterOutputStream; import javax.imageio.ImageIO; /** - * + * Generates bin/*.dat file for images. * @author JPEXS */ public class ImageBinDataGenerator { + /** + * Generates data + * @param is Input stream + * @param os Output stream + * @param format Image format + * @throws IOException On I/O error + */ public void generateBinData(InputStream is, OutputStream os, ImageFormat format) throws IOException { byte[] inputData = Helper.readStream(is); BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(inputData)); @@ -58,9 +63,9 @@ public class ImageBinDataGenerator { w.writeUI16(bimg.getWidth()); w.writeUI16(bimg.getHeight()); - w.writeUI32(0); w.writeUI32(0); w.writeUI32(20 * bimg.getWidth()); + w.writeUI32(0); w.writeUI32(20 * bimg.getHeight()); w.write(0x01); //has transparency @@ -72,10 +77,27 @@ public class ImageBinDataGenerator { for (int y = 0; y < bimg.getHeight(); y++) { for (int x = 0; x < bimg.getWidth(); x++) { int rgba = bimg.getRGB(x, y); - def.write((rgba >> 24) & 0xFF); //a - def.write((rgba >> 16) & 0xFF); //b - def.write((rgba >> 8) & 0xFF); //g - def.write(rgba & 0xFF); //r + int a = (rgba >> 24) & 0xFF; + int b = (rgba >> 16) & 0xFF; + int g = (rgba >> 8) & 0xFF; + int r = rgba & 0xFF; + + //some weird premultiplication + if (a != 255) { + r = (int)Math.floor(r * a / 256f); + g = (int)Math.floor(g * a / 256f); + b = (int)Math.floor(b * a / 256f); + } + + //also weird, but this way it works... + if (a != 0 && a != 255) { + a = a + 1; + } + + def.write(a); + def.write(b); + def.write(g); + def.write(r); } } def.flush(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/LosslessImageBinDataReader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/LosslessImageBinDataReader.java new file mode 100644 index 000000000..581f35f87 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/LosslessImageBinDataReader.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010-2024 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.xfl; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.InflaterInputStream; +import javax.imageio.ImageIO; + +/** + * Reads bin/*.dat file of lossless images and produces BufferedImage + * @author JPEXS + */ +public class LosslessImageBinDataReader { + + private final DataInputStream is; + + public LosslessImageBinDataReader(InputStream is) { + this.is = new DataInputStream(is); + } + + public BufferedImage readImage() throws IOException { + int sign1 = readEx(); + int sign2 = readEx(); + if (sign1 != 0x03 || sign2 != 0x05) { + throw new IOException("Invalid image"); + } + int rowSize = readUI16(); + int width = readUI16(); + int height = readUI16(); + long frameLeft = readUI32(); + long frameRight = readUI32(); + long frameTop = readUI32(); + long frameBottom = readUI32(); + int flags = readEx(); + + boolean hasAlpha = (flags & 1) == 1; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int variant = readEx(); + if (variant == 1) { //compressed + while (true) { + int chunkLen = readUI16(); + if (chunkLen == 0) { + break; + } + byte[] chunk = new byte[chunkLen]; + is.readFully(chunk); + baos.write(chunk); + } + } + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + InflaterInputStream iis = new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray())); + byte[] buf = new byte[4096]; + int cnt; + while ((cnt = iis.read(buf)) > 0) { + baos2.write(buf, 0, cnt); + } + + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + byte[] data = baos2.toByteArray(); + int pos = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int a = data[pos++] & 0xFF; + int b = data[pos++] & 0xFF; + int g = data[pos++] & 0xFF; + int r = data[pos++] & 0xFF; + + if (a != 0 && a != 255) { + a = a - 1; + + r = (int)Math.floor(r * 256f / a); + g = (int)Math.floor(g * 256f / a); + b = (int)Math.floor(b * 256f / a); + } + + int rgba = r + (g << 8) + (b << 16) + (a << 24); + img.setRGB(x, y, rgba); + } + } + return img; + } + + private int readUI16() throws IOException { + return readEx() + (readEx() << 8); + } + + private long readUI32() throws IOException { + return (readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24)) & 0xffffffffL; + } + + private int readEx() throws IOException { + int ret = is.read(); + if (ret == -1) { + throw new IOException("Premature end of the file reached"); + } + return ret; + } + + public static void main(String[] args) throws IOException { + File f = new File("in.bin"); + LosslessImageBinDataReader r = new LosslessImageBinDataReader(new FileInputStream(f)); + BufferedImage i = r.readImage(); + ImageIO.write(i, "PNG", new File("out.png")); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/MovieBinDataGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/MovieBinDataGenerator.java index 334adf316..813228a05 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/MovieBinDataGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/MovieBinDataGenerator.java @@ -33,11 +33,15 @@ import java.io.OutputStream; import java.util.List; /** - * + * Generates bin/*.dat file for movies. * @author JPEXS */ public class MovieBinDataGenerator { + /** + * Generates empty bin data. + * @return Byte array + */ public byte[] generateEmptyBinData() { return new byte[]{ (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, @@ -56,6 +60,13 @@ public class MovieBinDataGenerator { }; } + /** + * Generates data + * @param is Input stream + * @param os Output stream + * @param fps SWF fps + * @throws IOException On I/O error + */ public void generateBinData(InputStream is, OutputStream os, float fps) throws IOException { BinDataOutputStream df = new BinDataOutputStream(os); FLVInputStream flvIs = new FLVInputStream(is);