/* * Copyright (C) 2010-2016 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; import com.jpexs.decompiler.flash.amf.amf3.Amf3OutputStream; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.amf.amf3.ObjectTypeSerializeHandler; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA; import com.jpexs.decompiler.flash.types.ARGB; import com.jpexs.decompiler.flash.types.BITMAPDATA; import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; import com.jpexs.decompiler.flash.types.BUTTONRECORD; import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; import com.jpexs.decompiler.flash.types.CLIPACTIONS; import com.jpexs.decompiler.flash.types.CLIPEVENTFLAGS; import com.jpexs.decompiler.flash.types.CXFORM; import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; import com.jpexs.decompiler.flash.types.FILLSTYLE; import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; import com.jpexs.decompiler.flash.types.FOCALGRADIENT; import com.jpexs.decompiler.flash.types.GLYPHENTRY; import com.jpexs.decompiler.flash.types.GRADIENT; import com.jpexs.decompiler.flash.types.GRADRECORD; import com.jpexs.decompiler.flash.types.KERNINGRECORD; import com.jpexs.decompiler.flash.types.LANGCODE; import com.jpexs.decompiler.flash.types.LINESTYLE; import com.jpexs.decompiler.flash.types.LINESTYLE2; import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE; import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; import com.jpexs.decompiler.flash.types.MORPHFOCALGRADIENT; import com.jpexs.decompiler.flash.types.MORPHGRADIENT; import com.jpexs.decompiler.flash.types.MORPHGRADRECORD; import com.jpexs.decompiler.flash.types.MORPHLINESTYLE; import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2; import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; import com.jpexs.decompiler.flash.types.PIX15; import com.jpexs.decompiler.flash.types.PIX24; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; import com.jpexs.decompiler.flash.types.SOUNDINFO; import com.jpexs.decompiler.flash.types.TEXTRECORD; import com.jpexs.decompiler.flash.types.ZONEDATA; import com.jpexs.decompiler.flash.types.ZONERECORD; import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; import com.jpexs.decompiler.flash.types.filters.BLURFILTER; import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; import com.jpexs.decompiler.flash.types.filters.FILTER; import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.utf8.Utf8Helper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; /** * Class for writing data into SWF file * * @author JPEXS */ public class SWFOutputStream extends OutputStream { private final OutputStream os; private final int version; private long pos = 0; private int bitPos = 0; private int tempByte = 0; public long getPos() { return pos; } /** * Constructor * * @param os OutputStream for writing data * @param version Version of SWF */ public SWFOutputStream(OutputStream os, int version) { this.version = version; this.os = os; } /** * Writes byte to the stream * * @param b byte to write * @throws IOException */ @Override public void write(int b) throws IOException { alignByte(); os.write(b); pos++; } @Override public void write(byte[] b) throws IOException { alignByte(); os.write(b); pos += b.length; } public void write(ByteArrayRange b) throws IOException { alignByte(); os.write(b.getArray(), b.getPos(), b.getLength()); pos += b.getLength(); } private void alignByte() throws IOException { if (bitPos > 0) { bitPos = 0; write(tempByte); tempByte = 0; } } /** * Writes UI8 (Unsigned 8bit integer) value to the stream * * @param value UI8 value to write * @throws IOException */ public void writeUI8(int value) throws IOException { if (value > 0xff) { throw new IllegalArgumentException("Value is too large for UI8: " + value); } write(value); } /** * Writes null terminated string value to the stream * * @param value String value * @throws IOException */ public void writeString(String value) throws IOException { byte[] data = Utf8Helper.getBytes(value); for (int i = 0; i < data.length; i++) { if (data[i] == 0) { throw new IOException("String should not contain null character."); } } write(data); write(0); } /** * Writes netstring (length + string) value to the stream * * @param value String value * @throws IOException */ public void writeNetString(String value) throws IOException { byte[] data = value.getBytes(); writeUI8(data.length); write(data); } /** * Writes netstring (length + string) value to the stream * * @param value String value * @param charset * @throws IOException */ public void writeNetString(String value, Charset charset) throws IOException { byte[] data = value.getBytes(charset); writeUI8(data.length); write(data); } /** * Writes UI32 (Unsigned 32bit integer) value to the stream * * @param value UI32 value * @throws IOException */ public void writeUI32(long value) throws IOException { if (value > 0xffffffffL) { throw new IllegalArgumentException("Value is too large for UI32: " + value); } write((int) (value & 0xff)); write((int) ((value >> 8) & 0xff)); write((int) ((value >> 16) & 0xff)); write((int) ((value >> 24) & 0xff)); } /** * Writes UI16 (Unsigned 16bit integer) value to the stream * * @param value UI16 value * @throws IOException */ public void writeUI16(int value) throws IOException { if (value > 0xffff) { throw new IllegalArgumentException("Value is too large for UI16: " + value); } write((int) (value & 0xff)); write((int) ((value >> 8) & 0xff)); } /** * Writes SI32 (Signed 32bit integer) value to the stream * * @param value SI32 value * @throws IOException */ public void writeSI32(long value) throws IOException { if (value > 0x7fffffffL) { throw new IllegalArgumentException("Value is too large for SI32: " + value); } writeUI32(value); } /** * Writes SI16 (Signed 16bit integer) value to the stream * * @param value SI16 value * @throws IOException */ public void writeSI16(int value) throws IOException { if (value > 0x7fff) { Logger.getLogger(SWFOutputStream.class.getName()).log(Level.WARNING, "Value is too large for SI16: " + value + ", 0 written", new Exception()); value = 0; } writeUI16(value); } /** * Writes SI8 (Signed 8bit integer) value to the stream * * @param value SI8 value * @throws IOException */ public void writeSI8(int value) throws IOException { if (value > 0x7ff) { throw new IllegalArgumentException("Value is too large for SI8: " + value); } writeUI8(value); } /** * Writes FIXED (Fixed point 16.16) value to the stream * * @param value FIXED value * @throws IOException */ public void writeFIXED(double value) throws IOException { long valueLong = (long) (value * (1 << 16)); int beforePoint = (int) valueLong >> 16; int afterPoint = (int) valueLong % (1 << 16); writeUI16(afterPoint); writeUI16(beforePoint); } /** * Writes FIXED8 (Fixed point 8.8) value to the stream * * @param value FIXED8 value * @throws IOException */ public void writeFIXED8(float value) throws IOException { final int divisor = 1 << 8; int beforePoint = (int) value; int afterPoint = Math.abs((int) (value * divisor)) % divisor; writeUI8(afterPoint); writeSI8(beforePoint); } private void writeLong(long value) throws IOException { byte[] writeBuffer = new byte[8]; writeBuffer[3] = (byte) (value >>> 56); writeBuffer[2] = (byte) (value >>> 48); writeBuffer[1] = (byte) (value >>> 40); writeBuffer[0] = (byte) (value >>> 32); writeBuffer[7] = (byte) (value >>> 24); writeBuffer[6] = (byte) (value >>> 16); writeBuffer[5] = (byte) (value >>> 8); writeBuffer[4] = (byte) (value); write(writeBuffer); } /** * Writes DOUBLE (double precision floating point value) value to the stream * * @param value DOUBLE value * @throws IOException */ public void writeDOUBLE(double value) throws IOException { writeLong(Double.doubleToLongBits(value)); } /** * Writes FLOAT (single precision floating point value) value to the stream * * @param value FLOAT value * @throws IOException */ public void writeFLOAT(float value) throws IOException { writeUI32(Float.floatToIntBits(value)); } /** * Writes FLOAT16 (16bit floating point value) value to the stream * * @param value FLOAT16 value * @throws IOException */ public void writeFLOAT16(float value) throws IOException { int bits = Float.floatToRawIntBits(value); int sign = bits >> 31; int exponent = (bits >> 22) & 0xff; int mantisa = bits & 0x3FFFFF; mantisa >>= 13; writeUI16((sign << 15) + (exponent << 10) + mantisa); } /** * Writes EncodedU32 (Encoded unsigned 32bit value) value to the stream * * @param value U32 value * @throws IOException */ public void writeEncodedU32(long value) throws IOException { boolean loop = true; value &= 0xFFFFFFFF; do { int ret = (int) (value & 0x7F); if (value < 0x80) { loop = false; } if (value > 0x7F) { ret += 0x80; } write(ret); value >>= 7; } while (loop); } /** * Flushes data to underlying stream * * @throws IOException */ @Override public void flush() throws IOException { if (bitPos > 0) { bitPos = 0; write(tempByte); tempByte = 0; } os.flush(); } /** * Closes the stream * * @throws IOException */ @Override public void close() throws IOException { flush(); os.close(); } /** * Writes UB[nBits] (Unsigned-bit value) value to the stream * * @param nBits Number of bits which represent value * @param value Unsigned value to write * @throws IOException */ public void writeUB(int nBits, long value) throws IOException { for (int bit = 0; bit < nBits; bit++) { int nb = (int) ((value >> (nBits - 1 - bit)) & 1); tempByte += nb * (1 << (7 - bitPos)); bitPos++; if (bitPos == 8) { bitPos = 0; write(tempByte); tempByte = 0; } } } /** * Writes SB[nBits] (Signed-bit value) value to the stream * * @param nBits Number of bits which represent value * @param value Signed value to write * @throws IOException */ public void writeSB(int nBits, long value) throws IOException { writeUB(nBits, value); } /** * Writes FB[nBits] (Signed fixed-point bit value) value to the stream * * @param nBits Number of bits which represent value * @param value Double value to write * @throws IOException */ public void writeFB(int nBits, double value) throws IOException { if (nBits == 0) { return; } long longVal = (long) (value * (1 << 16)); writeSB(nBits, longVal); } /** * Writes RECT value to the stream * * @param value RECT value * @throws IOException */ public void writeRECT(RECT value) throws IOException { int nBits = 0; if (Configuration._debugCopy.get()) { nBits = Math.max(nBits, value.nbits); } int xMin = truncateTo31Bit(value.Xmin); int xMax = truncateTo31Bit(value.Xmax); int yMin = truncateTo31Bit(value.Ymin); int yMax = truncateTo31Bit(value.Ymax); nBits = enlargeBitCountS(nBits, xMin); nBits = enlargeBitCountS(nBits, xMax); nBits = enlargeBitCountS(nBits, yMin); nBits = enlargeBitCountS(nBits, yMax); if (Configuration._debugCopy.get()) { nBits = Math.max(nBits, value.nbits); } writeUB(5, nBits); writeSB(nBits, xMin); writeSB(nBits, xMax); writeSB(nBits, yMin); writeSB(nBits, yMax); alignByte(); } private int truncateTo31Bit(int value) { if (value > 0x3fffffff) { value = 0x3fffffff; } if (value < -0x3fffffff) { value = -0x3fffffff; } return value; } /** * Writes list of Tag values to the stream * * @param tags List of tag values * @throws IOException */ public void writeTags(Iterable<Tag> tags) throws IOException { for (Tag tag : tags) { tag.writeTag(this); } } /** * Calculates number of bits needed for representing unsigned value * * @param value Unsigned value * @return Number of bits */ public static int getNeededBitsU(int value) { if (value == 0) { return 0; } value = Math.abs(value); long x = 1; int nBits; for (nBits = 1; nBits <= 64; nBits++) { x <<= 1; if (x > value) { break; } } return nBits; } /** * Calculates number of bits needed for representing signed value * * @param v Signed value * @return Number of bits */ private static int getNeededBitsS(int v) { int counter = 32; int mask = 0x80000000; final int val = (v < 0) ? -v : v; while (((val & mask) == 0) && (counter > 0)) { mask >>>= 1; counter -= 1; } return counter + 1; } /** * Calculates number of bits needed for representing signed values * * @param first First Signed value * @param params Next Signed values * @return Number of bits */ public static int getNeededBitsS(int first, int... params) { int nBits = 0; nBits = enlargeBitCountS(nBits, first); for (int i = 0; i < params.length; i++) { nBits = enlargeBitCountS(nBits, params[i]); } return nBits; } public static int getNeededBitsU(int first, int... params) { int nBits = 0; nBits = enlargeBitCountU(nBits, first); for (int i = 0; i < params.length; i++) { nBits = enlargeBitCountU(nBits, params[i]); } return nBits; } public static int unsignedSize(final int value) { final int val = (value < 0) ? -value - 1 : value; int counter = 32; int mask = 0x80000000; while (((val & mask) == 0) && (counter > 0)) { mask >>>= 1; counter -= 1; } return counter; } /** * Calculates number of bits needed for representing fixed-point value * * @param value Fixed-point value * @return Number of bits */ public static int getNeededBitsF(float value) { // 0.26213074 16bits // 0.5 17bits // 1.3476715 18bits int k = (int) value; return getNeededBitsS(k) + 16; } public static int enlargeBitCountS(int currentBitCount, int value) { if (value == 0) { return currentBitCount; } int neededNew = getNeededBitsS(value); if (neededNew > currentBitCount) { return neededNew; } return currentBitCount; } public static int enlargeBitCountU(int currentBitCount, int value) { if (value == 0) { return currentBitCount; } int neededNew = getNeededBitsU(value); if (neededNew > currentBitCount) { return neededNew; } return currentBitCount; } /** * Writes MATRIX value to the stream * * @param value MATRIX value * @throws IOException */ public void writeMatrix(MATRIX value) throws IOException { writeUB(1, value.hasScale ? 1 : 0); if (value.hasScale) { int nBits = 0; nBits = enlargeBitCountS(nBits, value.scaleX); nBits = enlargeBitCountS(nBits, value.scaleY); if (Configuration._debugCopy.get()) { nBits = Math.max(nBits, value.nScaleBits); } writeUB(5, nBits); writeSB(nBits, value.scaleX); writeSB(nBits, value.scaleY); } writeUB(1, value.hasRotate ? 1 : 0); if (value.hasRotate) { int nBits = 0; nBits = enlargeBitCountS(nBits, value.rotateSkew0); nBits = enlargeBitCountS(nBits, value.rotateSkew1); if (Configuration._debugCopy.get()) { nBits = Math.max(nBits, value.nRotateBits); } writeUB(5, nBits); writeSB(nBits, value.rotateSkew0); writeSB(nBits, value.rotateSkew1); } int NTranslateBits = 0; NTranslateBits = enlargeBitCountS(NTranslateBits, value.translateX); NTranslateBits = enlargeBitCountS(NTranslateBits, value.translateY); if (Configuration._debugCopy.get()) { NTranslateBits = Math.max(NTranslateBits, value.nTranslateBits); } writeUB(5, NTranslateBits); writeSB(NTranslateBits, value.translateX); writeSB(NTranslateBits, value.translateY); alignByte(); } /** * Writes CXFORM value to the stream * * @param value CXFORM value * @throws IOException */ public void writeCXFORM(CXFORM value) throws IOException { writeUB(1, value.hasAddTerms ? 1 : 0); writeUB(1, value.hasMultTerms ? 1 : 0); int Nbits = 1; if (value.hasMultTerms) { Nbits = enlargeBitCountS(Nbits, value.redMultTerm); Nbits = enlargeBitCountS(Nbits, value.greenMultTerm); Nbits = enlargeBitCountS(Nbits, value.blueMultTerm); } if (value.hasAddTerms) { Nbits = enlargeBitCountS(Nbits, value.redAddTerm); Nbits = enlargeBitCountS(Nbits, value.greenAddTerm); Nbits = enlargeBitCountS(Nbits, value.blueAddTerm); } if (Configuration._debugCopy.get()) { Nbits = Math.max(Nbits, value.nbits); } writeUB(4, Nbits); if (value.hasMultTerms) { writeSB(Nbits, value.redMultTerm); writeSB(Nbits, value.greenMultTerm); writeSB(Nbits, value.blueMultTerm); } if (value.hasAddTerms) { writeSB(Nbits, value.redAddTerm); writeSB(Nbits, value.greenAddTerm); writeSB(Nbits, value.blueAddTerm); } alignByte(); } /** * Writes CXFORMWITHALPHA value to the stream * * @param value CXFORMWITHALPHA value * @throws IOException */ public void writeCXFORMWITHALPHA(CXFORMWITHALPHA value) throws IOException { writeUB(1, value.hasAddTerms ? 1 : 0); writeUB(1, value.hasMultTerms ? 1 : 0); int Nbits = 1; if (value.hasMultTerms) { Nbits = enlargeBitCountS(Nbits, value.redMultTerm); Nbits = enlargeBitCountS(Nbits, value.greenMultTerm); Nbits = enlargeBitCountS(Nbits, value.blueMultTerm); Nbits = enlargeBitCountS(Nbits, value.alphaMultTerm); } if (value.hasAddTerms) { Nbits = enlargeBitCountS(Nbits, value.redAddTerm); Nbits = enlargeBitCountS(Nbits, value.greenAddTerm); Nbits = enlargeBitCountS(Nbits, value.blueAddTerm); Nbits = enlargeBitCountS(Nbits, value.alphaAddTerm); } if (Configuration._debugCopy.get()) { Nbits = Math.max(Nbits, value.nbits); } writeUB(4, Nbits); if (value.hasMultTerms) { writeSB(Nbits, value.redMultTerm); writeSB(Nbits, value.greenMultTerm); writeSB(Nbits, value.blueMultTerm); writeSB(Nbits, value.alphaMultTerm); } if (value.hasAddTerms) { writeSB(Nbits, value.redAddTerm); writeSB(Nbits, value.greenAddTerm); writeSB(Nbits, value.blueAddTerm); writeSB(Nbits, value.alphaAddTerm); } alignByte(); } /** * Writes CLIPEVENTFLAGS value to the stream * * @param value CLIPEVENTFLAGS value * @throws IOException */ public void writeCLIPEVENTFLAGS(CLIPEVENTFLAGS value) throws IOException { writeUB(1, value.clipEventKeyUp ? 1 : 0); writeUB(1, value.clipEventKeyDown ? 1 : 0); writeUB(1, value.clipEventMouseUp ? 1 : 0); writeUB(1, value.clipEventMouseDown ? 1 : 0); writeUB(1, value.clipEventMouseMove ? 1 : 0); writeUB(1, value.clipEventUnload ? 1 : 0); writeUB(1, value.clipEventEnterFrame ? 1 : 0); writeUB(1, value.clipEventLoad ? 1 : 0); writeUB(1, value.clipEventDragOver ? 1 : 0); writeUB(1, value.clipEventRollOut ? 1 : 0); writeUB(1, value.clipEventRollOver ? 1 : 0); writeUB(1, value.clipEventReleaseOutside ? 1 : 0); writeUB(1, value.clipEventRelease ? 1 : 0); writeUB(1, value.clipEventPress ? 1 : 0); writeUB(1, value.clipEventInitialize ? 1 : 0); writeUB(1, value.clipEventData ? 1 : 0); if (version >= 6) { writeUB(5, value.reserved); writeUB(1, value.clipEventConstruct ? 1 : 0); writeUB(1, value.clipEventKeyPress ? 1 : 0); writeUB(1, value.clipEventDragOut ? 1 : 0); writeUB(8, value.reserved2); } } /** * Writes CLIPACTIONRECORD value to the stream * * @param value CLIPACTIONRECORD value * @throws IOException */ public void writeCLIPACTIONRECORD(CLIPACTIONRECORD value) throws IOException { writeCLIPEVENTFLAGS(value.eventFlags); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (SWFOutputStream sos = new SWFOutputStream(baos, version)) { if (value.eventFlags.clipEventKeyPress) { sos.writeUI8(value.keyCode); } sos.write(value.actionBytes); } byte[] data = baos.toByteArray(); writeUI32(data.length); // actionRecordSize write(data); } /** * Writes CLIPACTIONS value to the stream * * @param value CLIPACTIONS value * @throws IOException */ public void writeCLIPACTIONS(CLIPACTIONS value) throws IOException { writeUI16(value.reserved); writeCLIPEVENTFLAGS(value.allEventFlags); for (CLIPACTIONRECORD car : value.clipActionRecords) { writeCLIPACTIONRECORD(car); } if (version <= 5) { writeUI16(0); } else { writeUI32(0); } } /** * Writes COLORMATRIXFILTER value to the stream * * @param value COLORMATRIXFILTER value * @throws IOException */ public void writeCOLORMATRIXFILTER(COLORMATRIXFILTER value) throws IOException { for (int i = 0; i < 20; i++) { writeFLOAT(value.matrix[i]); } } /** * Writes RGBA value to the stream * * @param value RGBA value * @throws IOException */ public void writeRGBA(RGBA value) throws IOException { writeUI8(value.red); writeUI8(value.green); writeUI8(value.blue); writeUI8(value.alpha); } /** * Writes ARGB value to the stream * * @param value ARGB value * @throws IOException */ public void writeARGB(ARGB value) throws IOException { writeUI8(value.alpha); writeUI8(value.red); writeUI8(value.green); writeUI8(value.blue); } /** * Writes ARGB value to the stream * * @param value ARGB value * @throws IOException */ public void writeARGB(int value) throws IOException { writeUI8((value >> 24) & 0xff); writeUI8((value >> 16) & 0xff); writeUI8((value >> 8) & 0xff); writeUI8(value & 0xff); } /** * Writes RGB value to the stream * * @param value RGB value * @throws IOException */ public void writeRGB(RGB value) throws IOException { writeUI8(value.red); writeUI8(value.green); writeUI8(value.blue); } /** * Writes CONVOLUTIONFILTER value to the stream * * @param value CONVOLUTIONFILTER value * @throws IOException */ public void writeCONVOLUTIONFILTER(CONVOLUTIONFILTER value) throws IOException { writeUI8(value.matrixX); writeUI8(value.matrixY); writeFLOAT(value.divisor); writeFLOAT(value.bias); for (int x = 0; x < value.matrixX; x++) { for (int y = 0; y < value.matrixY; y++) { writeFLOAT(value.matrix[x][y]); } } writeRGBA(value.defaultColor); writeUB(6, value.reserved); writeUB(1, value.clamp ? 1 : 0); writeUB(1, value.preserveAlpha ? 1 : 0); } /** * Writes BLURFILTER value to the stream * * @param value BLURFILTER value * @throws IOException */ public void writeBLURFILTER(BLURFILTER value) throws IOException { writeFIXED(value.blurX); writeFIXED(value.blurY); writeUB(5, value.passes); writeUB(3, value.reserved); } /** * Writes DROPSHADOWFILTER value to the stream * * @param value DROPSHADOWFILTER value * @throws IOException */ public void writeDROPSHADOWFILTER(DROPSHADOWFILTER value) throws IOException { writeRGBA(value.dropShadowColor); writeFIXED(value.blurX); writeFIXED(value.blurY); writeFIXED(value.angle); writeFIXED(value.distance); writeFIXED8(value.strength); writeUB(1, value.innerShadow ? 1 : 0); writeUB(1, value.knockout ? 1 : 0); writeUB(1, value.compositeSource ? 1 : 0); writeUB(5, value.passes); } /** * Writes GLOWFILTER value to the stream * * @param value GLOWFILTER value * @throws IOException */ public void writeGLOWFILTER(GLOWFILTER value) throws IOException { writeRGBA(value.glowColor); writeFIXED(value.blurX); writeFIXED(value.blurY); writeFIXED8(value.strength); writeUB(1, value.innerGlow ? 1 : 0); writeUB(1, value.knockout ? 1 : 0); writeUB(1, value.compositeSource ? 1 : 0); writeUB(5, value.passes); } /** * Writes BEVELFILTER value to the stream * * @param value BEVELFILTER value * @throws IOException */ public void writeBEVELFILTER(BEVELFILTER value) throws IOException { writeRGBA(value.highlightColor); writeRGBA(value.shadowColor); writeFIXED(value.blurX); writeFIXED(value.blurY); writeFIXED(value.angle); writeFIXED(value.distance); writeFIXED8(value.strength); writeUB(1, value.innerShadow ? 1 : 0); writeUB(1, value.knockout ? 1 : 0); writeUB(1, value.compositeSource ? 1 : 0); writeUB(1, value.onTop ? 1 : 0); writeUB(4, value.passes); } /** * Writes GRADIENTGLOWFILTER value to the stream * * @param value GRADIENTGLOWFILTER value * @throws IOException */ public void writeGRADIENTGLOWFILTER(GRADIENTGLOWFILTER value) throws IOException { writeUI8(value.gradientColors.length); for (int i = 0; i < value.gradientColors.length; i++) { writeRGBA(value.gradientColors[i]); } for (int i = 0; i < value.gradientColors.length; i++) { writeUI8(value.gradientRatio[i]); } writeFIXED(value.blurX); writeFIXED(value.blurY); writeFIXED(value.angle); writeFIXED(value.distance); writeFIXED8(value.strength); writeUB(1, value.innerShadow ? 1 : 0); writeUB(1, value.knockout ? 1 : 0); writeUB(1, value.compositeSource ? 1 : 0); writeUB(1, value.onTop ? 1 : 0); writeUB(4, value.passes); } /** * Writes GRADIENTBEVELFILTER value to the stream * * @param value GRADIENTBEVELFILTER value * @throws IOException */ public void writeGRADIENTBEVELFILTER(GRADIENTBEVELFILTER value) throws IOException { writeUI8(value.gradientColors.length); for (int i = 0; i < value.gradientColors.length; i++) { writeRGBA(value.gradientColors[i]); } for (int i = 0; i < value.gradientColors.length; i++) { writeUI8(value.gradientRatio[i]); } writeFIXED(value.blurX); writeFIXED(value.blurY); writeFIXED(value.angle); writeFIXED(value.distance); writeFIXED8(value.strength); writeUB(1, value.innerShadow ? 1 : 0); writeUB(1, value.knockout ? 1 : 0); writeUB(1, value.compositeSource ? 1 : 0); writeUB(1, value.onTop ? 1 : 0); writeUB(4, value.passes); } /** * Writes list of FILTER values to the stream * * @param list List of FILTER values * @throws IOException */ public void writeFILTERLIST(List<FILTER> list) throws IOException { writeUI8(list.size()); for (int i = 0; i < list.size(); i++) { writeFILTER(list.get(i)); } } /** * Writes FILTER value to the stream * * @param value FILTER value * @throws IOException */ public void writeFILTER(FILTER value) throws IOException { writeUI8(value.id); if (value instanceof DROPSHADOWFILTER) { writeDROPSHADOWFILTER((DROPSHADOWFILTER) value); } if (value instanceof BLURFILTER) { writeBLURFILTER((BLURFILTER) value); } if (value instanceof GLOWFILTER) { writeGLOWFILTER((GLOWFILTER) value); } if (value instanceof BEVELFILTER) { writeBEVELFILTER((BEVELFILTER) value); } if (value instanceof GRADIENTGLOWFILTER) { writeGRADIENTGLOWFILTER((GRADIENTGLOWFILTER) value); } if (value instanceof CONVOLUTIONFILTER) { writeCONVOLUTIONFILTER((CONVOLUTIONFILTER) value); } if (value instanceof COLORMATRIXFILTER) { writeCOLORMATRIXFILTER((COLORMATRIXFILTER) value); } if (value instanceof GRADIENTBEVELFILTER) { writeGRADIENTBEVELFILTER((GRADIENTBEVELFILTER) value); } } /** * Writes list of BUTTONRECORD values to the stream * * @param list List of BUTTONRECORD values * @param inDefineButton2 Whether write inside of DefineButton2Tag or not * @throws IOException */ public void writeBUTTONRECORDList(List<BUTTONRECORD> list, boolean inDefineButton2) throws IOException { for (BUTTONRECORD brec : list) { writeBUTTONRECORD(brec, inDefineButton2); } writeUI8(0); } /** * Writes BUTTONRECORD value to the stream * * @param value BUTTONRECORD value * @param inDefineButton2 Whether write inside of DefineButton2Tag or not * @throws IOException */ public void writeBUTTONRECORD(BUTTONRECORD value, boolean inDefineButton2) throws IOException { writeUB(2, value.reserved); writeUB(1, value.buttonHasBlendMode ? 1 : 0); writeUB(1, value.buttonHasFilterList ? 1 : 0); writeUB(1, value.buttonStateHitTest ? 1 : 0); writeUB(1, value.buttonStateDown ? 1 : 0); writeUB(1, value.buttonStateOver ? 1 : 0); writeUB(1, value.buttonStateUp ? 1 : 0); writeUI16(value.characterId); writeUI16(value.placeDepth); writeMatrix(value.placeMatrix); if (inDefineButton2) { writeCXFORMWITHALPHA(value.colorTransform); if (value.buttonHasFilterList) { writeFILTERLIST(value.filterList); } if (value.buttonHasBlendMode) { writeUI8(value.blendMode); } } } /** * Writes list of BUTTONCONDACTION values to the stream * * @param list List of BUTTONCONDACTION values * @throws IOException */ public void writeBUTTONCONDACTIONList(List<BUTTONCONDACTION> list) throws IOException { for (int i = 0; i < list.size(); i++) { writeBUTTONCONDACTION(list.get(i), i == list.size() - 1); } } /** * Writes BUTTONCONDACTION value to the stream * * @param value BUTTONCONDACTION value * @param isLast True if it is last on the list * @throws IOException */ public void writeBUTTONCONDACTION(BUTTONCONDACTION value, boolean isLast) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (SWFOutputStream sos = new SWFOutputStream(baos, version)) { sos.writeUB(1, value.condIdleToOverDown ? 1 : 0); sos.writeUB(1, value.condOutDownToIdle ? 1 : 0); sos.writeUB(1, value.condOutDownToOverDown ? 1 : 0); sos.writeUB(1, value.condOverDownToOutDown ? 1 : 0); sos.writeUB(1, value.condOverDownToOverUp ? 1 : 0); sos.writeUB(1, value.condOverUpToOverDown ? 1 : 0); sos.writeUB(1, value.condOverUpToIddle ? 1 : 0); sos.writeUB(1, value.condIdleToOverUp ? 1 : 0); sos.writeUB(7, value.condKeyPress); sos.writeUB(1, value.condOverDownToIdle ? 1 : 0); sos.write(value.actionBytes); } byte[] data = baos.toByteArray(); if (isLast) { writeUI16(0); } else { writeUI16(data.length + 2); } write(data); } /** * Writes FILLSTYLE value to the stream * * @param value FILLSTYLE value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeFILLSTYLE(FILLSTYLE value, int shapeNum) throws IOException { writeUI8(value.fillStyleType); if (value.fillStyleType == FILLSTYLE.SOLID) { if (shapeNum >= 3) { writeRGBA((RGBA) value.color); } else if (shapeNum == 1 || shapeNum == 2) { writeRGB(value.color); } } if ((value.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) || (value.fillStyleType == FILLSTYLE.RADIAL_GRADIENT) || (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT)) { writeMatrix(value.gradientMatrix); } if ((value.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) || (value.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)) { writeGRADIENT(value.gradient, shapeNum); } if (value.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { writeFOCALGRADIENT((FOCALGRADIENT) value.gradient, shapeNum); } if ((value.fillStyleType == FILLSTYLE.REPEATING_BITMAP) || (value.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) || (value.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) || (value.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { writeUI16(value.bitmapId); writeMatrix(value.bitmapMatrix); } } /** * Writes FILLSTYLEARRAY value to the stream * * @param value FILLSTYLEARRAY value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeFILLSTYLEARRAY(FILLSTYLEARRAY value, int shapeNum) throws IOException { int fillStyleCount = value.fillStyles.length; if (shapeNum > 1) { if (fillStyleCount >= 0xff) { writeUI8(0xff); writeUI16(fillStyleCount); } else { writeUI8(fillStyleCount); } } else { writeUI8(fillStyleCount); } for (int i = 0; i < value.fillStyles.length; i++) { writeFILLSTYLE(value.fillStyles[i], shapeNum); } } /** * Writes FOCALGRADIENT value to the stream * * @param value FILLSTYLEARRAY value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeFOCALGRADIENT(FOCALGRADIENT value, int shapeNum) throws IOException { writeUB(2, value.spreadMode); writeUB(2, value.interpolationMode); writeUB(4, value.gradientRecords.length); for (int i = 0; i < value.gradientRecords.length; i++) { writeGRADRECORD(value.gradientRecords[i], shapeNum); } writeFIXED8(value.focalPoint); } /** * Writes GRADIENT value to the stream * * @param value GRADIENT value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeGRADIENT(GRADIENT value, int shapeNum) throws IOException { writeUB(2, value.spreadMode); writeUB(2, value.interpolationMode); writeUB(4, value.gradientRecords.length); for (int i = 0; i < value.gradientRecords.length; i++) { writeGRADRECORD(value.gradientRecords[i], shapeNum); } } /** * Writes GRADRECORD value to the stream * * @param value GRADRECORD value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeGRADRECORD(GRADRECORD value, int shapeNum) throws IOException { writeUI8(value.ratio); if (shapeNum >= 3) { writeRGBA((RGBA) value.color); } else { writeRGB(value.color); } } /** * Writes LINESTYLE value to the stream * * @param value LINESTYLE value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeLINESTYLE(LINESTYLE value, int shapeNum) throws IOException { writeUI16(value.width); if (shapeNum == 1 || shapeNum == 2) { writeRGB(value.color); } else if (shapeNum == 3) { writeRGBA((RGBA) value.color); } } /** * Writes LINESTYLE2 value to the stream * * @param value LINESTYLE2 value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeLINESTYLE2(LINESTYLE2 value, int shapeNum) throws IOException { writeUI16(value.width); writeUB(2, value.startCapStyle); writeUB(2, value.joinStyle); writeUB(1, value.hasFillFlag ? 1 : 0); writeUB(1, value.noHScaleFlag ? 1 : 0); writeUB(1, value.noVScaleFlag ? 1 : 0); writeUB(1, value.pixelHintingFlag ? 1 : 0); writeUB(5, value.reserved); writeUB(1, value.noClose ? 1 : 0); writeUB(2, value.endCapStyle); if (value.joinStyle == LINESTYLE2.MITER_JOIN) { writeFIXED8(value.miterLimitFactor); } if (!value.hasFillFlag) { writeRGBA((RGBA) value.color); } else { writeFILLSTYLE(value.fillType, shapeNum); } } /** * Writes LINESTYLEARRAY value to the stream * * @param value FILLSTYLEARRAY value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeLINESTYLEARRAY(LINESTYLEARRAY value, int shapeNum) throws IOException { int lineStyleCount; if (shapeNum <= 3) { lineStyleCount = value.lineStyles.length; if (lineStyleCount >= 0xff) { writeUI8(0xff); writeUI16(lineStyleCount); } else { writeUI8(lineStyleCount); } for (int i = 0; i < lineStyleCount; i++) { writeLINESTYLE(value.lineStyles[i], shapeNum); } } else { lineStyleCount = value.lineStyles.length; if (lineStyleCount >= 0xff) { writeUI8(0xff); writeUI16(lineStyleCount); } else { writeUI8(lineStyleCount); } for (int i = 0; i < lineStyleCount; i++) { writeLINESTYLE2((LINESTYLE2) value.lineStyles[i], shapeNum); } } } /** * Writes SHAPE value to the stream * * @param value SHAPE value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeSHAPE(SHAPE value, int shapeNum) throws IOException { writeUB(4, value.numFillBits); writeUB(4, value.numLineBits); writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); } /** * Writes SHAPEWITHSTYLE value to the stream * * @param value SHAPEWITHSTYLE value * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ public void writeSHAPEWITHSTYLE(SHAPEWITHSTYLE value, int shapeNum) throws IOException { writeFILLSTYLEARRAY(value.fillStyles, shapeNum); writeLINESTYLEARRAY(value.lineStyles, shapeNum); value.numFillBits = getNeededBitsU(value.fillStyles.fillStyles.length); value.numLineBits = getNeededBitsU(value.lineStyles.lineStyles.length); writeUB(4, value.numFillBits); writeUB(4, value.numLineBits); writeSHAPERECORDS(value.shapeRecords, value.numFillBits, value.numLineBits, shapeNum); } /** * Writes SHAPERECORDs value to the stream * * @param value SHAPERECORDS value * @param fillBits * @param lineBits * @param shapeNum 1 in DefineShape, 2 in DefineShape2,... * @throws IOException */ private void writeSHAPERECORDS(List<SHAPERECORD> value, int fillBits, int lineBits, int shapeNum) throws IOException { for (SHAPERECORD sh : value) { if (sh instanceof CurvedEdgeRecord) { CurvedEdgeRecord cer = (CurvedEdgeRecord) sh; writeUB(1, 1); // typeFlag writeUB(1, 0); // curvedEdge int numBits = Math.max(getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2, 0); if (Configuration._debugCopy.get()) { numBits = Math.max(numBits, cer.numBits); } cer.numBits = numBits; writeUB(4, cer.numBits); writeSB(cer.numBits + 2, cer.controlDeltaX); writeSB(cer.numBits + 2, cer.controlDeltaY); writeSB(cer.numBits + 2, cer.anchorDeltaX); writeSB(cer.numBits + 2, cer.anchorDeltaY); } else if (sh instanceof StraightEdgeRecord) { StraightEdgeRecord ser = (StraightEdgeRecord) sh; writeUB(1, 1); // typeFlag writeUB(1, 1); // straightEdge int numBits = Math.max(getNeededBitsS(ser.deltaX, ser.deltaY) - 2, 0); if (Configuration._debugCopy.get()) { numBits = Math.max(numBits, ser.numBits); } ser.numBits = numBits; writeUB(4, ser.numBits); writeUB(1, ser.generalLineFlag ? 1 : 0); if (!ser.generalLineFlag) { writeUB(1, ser.vertLineFlag ? 1 : 0); } if (ser.generalLineFlag || (!ser.vertLineFlag)) { writeSB(ser.numBits + 2, ser.deltaX); } if (ser.generalLineFlag || ser.vertLineFlag) { writeSB(ser.numBits + 2, ser.deltaY); } } else if (sh instanceof StyleChangeRecord) { StyleChangeRecord scr = (StyleChangeRecord) sh; writeUB(1, 0); // typeFlag writeUB(1, scr.stateNewStyles ? 1 : 0); writeUB(1, scr.stateLineStyle ? 1 : 0); writeUB(1, scr.stateFillStyle1 ? 1 : 0); writeUB(1, scr.stateFillStyle0 ? 1 : 0); writeUB(1, scr.stateMoveTo ? 1 : 0); if (scr.stateMoveTo) { int moveBits = getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); if (Configuration._debugCopy.get()) { moveBits = Math.max(moveBits, scr.moveBits); } scr.moveBits = moveBits; writeUB(5, scr.moveBits); writeSB(scr.moveBits, scr.moveDeltaX); writeSB(scr.moveBits, scr.moveDeltaY); } if (scr.stateFillStyle0) { writeUB(fillBits, scr.fillStyle0); } if (scr.stateFillStyle1) { writeUB(fillBits, scr.fillStyle1); } if (scr.stateLineStyle) { writeUB(lineBits, scr.lineStyle); } if (scr.stateNewStyles) { writeFILLSTYLEARRAY(scr.fillStyles, shapeNum); writeLINESTYLEARRAY(scr.lineStyles, shapeNum); fillBits = getNeededBitsU(scr.fillStyles.fillStyles.length); lineBits = getNeededBitsU(scr.lineStyles.lineStyles.length); if (Configuration._debugCopy.get()) { fillBits = Math.max(fillBits, scr.numFillBits); lineBits = Math.max(lineBits, scr.numLineBits); } scr.numFillBits = fillBits; scr.numLineBits = lineBits; writeUB(4, scr.numFillBits); writeUB(4, scr.numLineBits); } } else if (sh instanceof EndShapeRecord) { writeUB(1, 0); // typeFlag writeUB(5, 0); // end of shape flag } } alignByte(); } /** * Writes SOUNDINFO value to the stream * * @param value SOUNDINFO value * @throws IOException */ public void writeSOUNDINFO(SOUNDINFO value) throws IOException { writeUB(2, value.reserved); writeUB(1, value.syncStop ? 1 : 0); writeUB(1, value.syncNoMultiple ? 1 : 0); writeUB(1, value.hasEnvelope ? 1 : 0); writeUB(1, value.hasLoops ? 1 : 0); writeUB(1, value.hasOutPoint ? 1 : 0); writeUB(1, value.hasInPoint ? 1 : 0); if (value.hasInPoint) { writeUI32(value.inPoint); } if (value.hasOutPoint) { writeUI32(value.outPoint); } if (value.hasLoops) { writeUI16(value.loopCount); } if (value.hasEnvelope) { writeUI8(value.envelopeRecords.length); for (SOUNDENVELOPE env : value.envelopeRecords) { writeSOUNDENVELOPE(env); } } } /** * Writes SOUNDENVELOPE value to the stream * * @param value SOUNDENVELOPE value * @throws IOException */ public void writeSOUNDENVELOPE(SOUNDENVELOPE value) throws IOException { writeUI32(value.pos44); writeUI16(value.leftLevel); writeUI16(value.rightLevel); } /** * Writes TEXTRECORD value to the stream * * @param value TEXTRECORD value * @param defineTextNum * @param glyphBits * @param advanceBits * @throws IOException */ public void writeTEXTRECORD(TEXTRECORD value, int defineTextNum, int glyphBits, int advanceBits) throws IOException { writeUB(1, 1); writeUB(3, 0); writeUB(1, value.styleFlagsHasFont ? 1 : 0); writeUB(1, value.styleFlagsHasColor ? 1 : 0); writeUB(1, value.styleFlagsHasYOffset ? 1 : 0); writeUB(1, value.styleFlagsHasXOffset ? 1 : 0); if (value.styleFlagsHasFont) { writeUI16(value.fontId); } if (value.styleFlagsHasColor) { if (defineTextNum == 2) { writeRGBA(value.textColorA); } else { writeRGB(value.textColor); } } if (value.styleFlagsHasXOffset) { writeSI16(value.xOffset); } if (value.styleFlagsHasYOffset) { writeSI16(value.yOffset); } if (value.styleFlagsHasFont) { writeUI16(value.textHeight); } writeUI8(value.glyphEntries.size()); for (GLYPHENTRY ge : value.glyphEntries) { writeGLYPHENTRY(ge, glyphBits, advanceBits); } alignByte(); } /** * Writes GLYPHENTRY value to the stream * * @param value GLYPHENTRY value * @param glyphBits * @param advanceBits * @throws IOException */ public void writeGLYPHENTRY(GLYPHENTRY value, int glyphBits, int advanceBits) throws IOException { writeUB(glyphBits, value.glyphIndex); writeSB(advanceBits, value.glyphAdvance); } /** * Writes MORPHFILLSTYLE value to the stream * * @param value MORPHFILLSTYLE value * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... * @throws IOException */ public void writeMORPHFILLSTYLE(MORPHFILLSTYLE value, int shapeNum) throws IOException { writeUI8(value.fillStyleType); if (value.fillStyleType == MORPHFILLSTYLE.SOLID) { writeRGBA(value.startColor); writeRGBA(value.endColor); } if ((value.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) || (value.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT) || (value.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT)) { writeMatrix(value.startGradientMatrix); writeMatrix(value.endGradientMatrix); } if ((value.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT) || (value.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)) { writeMORPHGRADIENT(value.gradient, shapeNum); } if (value.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT) { writeMORPHFOCALGRADIENT((MORPHFOCALGRADIENT) value.gradient, shapeNum); } if ((value.fillStyleType == MORPHFILLSTYLE.REPEATING_BITMAP) || (value.fillStyleType == MORPHFILLSTYLE.CLIPPED_BITMAP) || (value.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP) || (value.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { writeUI16(value.bitmapId); writeMatrix(value.startBitmapMatrix); writeMatrix(value.endBitmapMatrix); } } /** * WritesMORPH FILLSTYLEARRAY value to the stream * * @param value MORPHFILLSTYLEARRAY value * @param morphShapeNum 1 on DefineMorphShape, 2 on DefineMorphShape * @throws IOException */ public void writeMORPHFILLSTYLEARRAY(MORPHFILLSTYLEARRAY value, int morphShapeNum) throws IOException { int fillStyleCount = value.fillStyles.length; if (fillStyleCount >= 0xff) { writeUI8(0xff); writeUI16(fillStyleCount); } else { writeUI8(fillStyleCount); } for (int i = 0; i < value.fillStyles.length; i++) { writeMORPHFILLSTYLE(value.fillStyles[i], morphShapeNum); } } /** * Writes MORPHGRADIENT value to the stream * * @param value MORPHGRADIENT value * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... * @throws IOException */ public void writeMORPHGRADIENT(MORPHGRADIENT value, int shapeNum) throws IOException { // Despite of documentation (UI8 1-8), there are two fields // spreadMode and interPolationMode which are same as in GRADIENT writeUB(2, value.spreadMode); writeUB(2, value.interPolationMode); writeUB(4, value.gradientRecords.length); for (int i = 0; i < value.gradientRecords.length; i++) { writeMORPHGRADRECORD(value.gradientRecords[i]); } } /** * Writes MORPHFOCALGRADIENT value to the stream * * Undocumented feature * * @param value MORPHGRADIENT value * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... * @throws IOException */ public void writeMORPHFOCALGRADIENT(MORPHFOCALGRADIENT value, int shapeNum) throws IOException { writeUB(2, value.spreadMode); writeUB(2, value.interPolationMode); writeUB(4, value.gradientRecords.length); for (int i = 0; i < value.gradientRecords.length; i++) { writeMORPHGRADRECORD(value.gradientRecords[i]); } writeFIXED8(value.startFocalPoint); writeFIXED8(value.endFocalPoint); } /** * Writes MORPHGRADRECORD value to the stream * * @param value MORPHGRADRECORD value * @throws IOException */ public void writeMORPHGRADRECORD(MORPHGRADRECORD value) throws IOException { writeUI8(value.startRatio); writeRGBA(value.startColor); writeUI8(value.endRatio); writeRGBA(value.endColor); } /** * Writes MORPHLINESTYLE value to the stream * * @param value LINESTYLE value * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... * @throws IOException */ public void writeMORPHLINESTYLE(MORPHLINESTYLE value, int shapeNum) throws IOException { writeUI16(value.startWidth); writeUI16(value.endWidth); writeRGBA(value.startColor); writeRGBA(value.endColor); } /** * Writes MORPHLINESTYLE2 value to the stream * * @param value MORPHLINESTYLE2 value * @param shapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... * @throws IOException */ public void writeMORPHLINESTYLE2(MORPHLINESTYLE2 value, int shapeNum) throws IOException { writeUI16(value.startWidth); writeUI16(value.endWidth); writeUB(2, value.startCapStyle); writeUB(2, value.joinStyle); writeUB(1, value.hasFillFlag ? 1 : 0); writeUB(1, value.noHScaleFlag ? 1 : 0); writeUB(1, value.noVScaleFlag ? 1 : 0); writeUB(1, value.pixelHintingFlag ? 1 : 0); writeUB(5, value.reserved); writeUB(1, value.noClose ? 1 : 0); writeUB(2, value.endCapStyle); if (value.joinStyle == LINESTYLE2.MITER_JOIN) { writeUI16(value.miterLimitFactor); } if (!value.hasFillFlag) { writeRGBA(value.startColor); writeRGBA(value.endColor); } else { writeMORPHFILLSTYLE(value.fillType, shapeNum); } } /** * Writes MORPHLINESTYLEARRAY value to the stream * * @param value MORPHFILLSTYLEARRAY value * @param morphShapeNum 1 in DefineMorphShape, 2 in DefineMorphShape2,... * @throws IOException */ public void writeMORPHLINESTYLEARRAY(MORPHLINESTYLEARRAY value, int morphShapeNum) throws IOException { int lineStyleCount; if (morphShapeNum == 1) { lineStyleCount = value.lineStyles.length; if (lineStyleCount >= 0xff) { writeUI8(0xff); writeUI16(lineStyleCount); } else { writeUI8(lineStyleCount); } for (int i = 0; i < lineStyleCount; i++) { writeMORPHLINESTYLE(value.lineStyles[i], morphShapeNum); } } else if (morphShapeNum == 2) { lineStyleCount = value.lineStyles2.length; if (lineStyleCount >= 0xff) { writeUI8(0xff); writeUI16(lineStyleCount); } else { writeUI8(lineStyleCount); } for (int i = 0; i < lineStyleCount; i++) { writeMORPHLINESTYLE2(value.lineStyles2[i], morphShapeNum); } } } /** * Writes KERNINGRECORD value to the stream * * @param value KERNINGRECORD value * @param fontFlagsWideCodes * @throws IOException */ public void writeKERNINGRECORD(KERNINGRECORD value, boolean fontFlagsWideCodes) throws IOException { if (fontFlagsWideCodes) { writeUI16(value.fontKerningCode1); writeUI16(value.fontKerningCode2); } else { writeUI8(value.fontKerningCode1); writeUI8(value.fontKerningCode2); } writeSI16(value.fontKerningAdjustment); } /** * Writes LANGCODE value to the stream * * @param value LANGCODE value * @throws IOException */ public void writeLANGCODE(LANGCODE value) throws IOException { writeUI8(value.languageCode); } /** * Writes ZONERECORD value to the stream * * @param value ZONERECORD value * @throws IOException */ public void writeZONERECORD(ZONERECORD value) throws IOException { writeUI8(value.zonedata.length); for (int i = 0; i < value.zonedata.length; i++) { writeZONEDATA(value.zonedata[i]); } writeUB(6, 0); writeUB(1, value.zoneMaskY ? 1 : 0); writeUB(1, value.zoneMaskX ? 1 : 0); } /** * Writes ZONEDATA value to the stream * * @param value ZONEDATA value * @throws IOException */ public void writeZONEDATA(ZONEDATA value) throws IOException { writeUI16(value.alignmentCoordinate); writeUI16(value.range); } public void writeBytesZlib(byte[] data) throws IOException { DeflaterOutputStream deflater = new DeflaterOutputStream(this, new Deflater(9)); deflater.write(data); deflater.finish(); } public static byte[] compressByteArray(byte[] data) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DeflaterOutputStream deflater = new DeflaterOutputStream(baos, new Deflater(9)); deflater.write(data); deflater.finish(); return baos.toByteArray(); } /** * Reads one BITMAPDATA value from the stream * * @param value * @param bitmapFormat * @param bitmapWidth * @param bitmapHeight * @throws IOException */ public void writeBITMAPDATA(BITMAPDATA value, int bitmapFormat, int bitmapWidth, int bitmapHeight) throws IOException { int dataLen = 0; int pos = 0; for (int y = 0; y < bitmapHeight; y++) { int x = 0; for (; x < bitmapWidth; x++) { if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) { dataLen += 2; writePIX15(value.bitmapPixelDataPix15[pos]); } if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) { dataLen += 4; writePIX24(value.bitmapPixelDataPix24[pos]); } pos++; } while ((dataLen % 4) != 0) { dataLen++; writeUI8(0); } } } /** * Reads one ALPHABITMAPDATA value from the stream * * @param value * @param bitmapFormat * @param bitmapWidth * @param bitmapHeight * @throws IOException */ public void writeALPHABITMAPDATA(ALPHABITMAPDATA value, int bitmapFormat, int bitmapWidth, int bitmapHeight) throws IOException { int pos = 0; for (int y = 0; y < bitmapHeight; y++) { for (int x = 0; x < bitmapWidth; x++) { writeARGB(value.bitmapPixelData[pos]); pos++; } } } /** * Writes PIX24 value to the stream * * @param value PIX24 value * @throws IOException */ public void writePIX24(PIX24 value) throws IOException { writeUI8(value.reserved); writeUI8(value.red); writeUI8(value.green); writeUI8(value.blue); } /** * Writes PIX24 value to the stream * * @param value PIX24 value * @throws IOException */ public void writePIX24(int value) throws IOException { writeUI8((value >> 24) & 0xff); writeUI8((value >> 16) & 0xff); writeUI8((value >> 8) & 0xff); writeUI8(value & 0xff); } /** * Writes PIX15 value to the stream * * @param value PIX15 value * @throws IOException */ public void writePIX15(PIX15 value) throws IOException { writeUB(1, value.reserved); writeUB(5, value.red); writeUB(5, value.green); writeUB(5, value.blue); } /** * Writes PIX15 value to the stream * * @param value PIX15 value * @throws IOException */ public void writePIX15(int value) throws IOException { writeUB(1, (value >> 24) & 0xff); writeUB(5, (value >> 19) & 0xff); writeUB(5, (value >> 11) & 0xff); writeUB(5, (value >> 3) & 0xff); } /** * Writes AMF3 encoded value. Warning: Correct serializer needs to be passed * as second parameter when using IExternalizable * * @param value * @param serializers Map className=>Serializer for classes implementing * IExternalizable * @throws IOException * @throws NoSerializerExistsException */ public void writeAmf3Object(Amf3Value value, Map<String, ObjectTypeSerializeHandler> serializers) throws IOException, NoSerializerExistsException { Amf3OutputStream ao = new Amf3OutputStream(os); ao.writeValue(value.getValue(), serializers); } /** * Writes AMF3 encoded value. Warning: When the object implements * IExternalizable, you need to pass serializer as second parameter * * @param value * @throws IOException * @throws NoSerializerExistsException */ public void writeAmf3Object(Amf3Value value) throws IOException, NoSerializerExistsException { writeAmf3Object(value, new HashMap<>()); } }