#559 morphshapes as animated SVG/CSS3 (Currenly only the edges are morphed, the style is not ready)

This commit is contained in:
Honfika
2014-04-17 00:34:39 +02:00
parent 31a92d71e3
commit c3bf4963b0
25 changed files with 1425 additions and 118 deletions
+51 -20
View File
@@ -99,6 +99,7 @@ import com.jpexs.decompiler.flash.tags.base.ContainerItem;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
@@ -1479,24 +1480,6 @@ public final class SWF implements TreeItem, Timelined {
fos.write(chunkBytes);
}
//TODO: implement morphshape export. How to handle 65536 frames?
public List<File> exportMorphShapes(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, final MorphshapeExportMode mode) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
if (!foutdir.exists()) {
if (!foutdir.mkdirs()) {
if (!foutdir.exists()) {
throw new IOException("Cannot create directory " + outdir);
}
}
}
throw new UnsupportedOperationException("Not implemented");
//return ret;
}
private static void makeAVI(List<BufferedImage> images, int frameRate, File file) throws IOException {
if (images.isEmpty()) {
return;
@@ -1840,7 +1823,7 @@ public final class SWF implements TreeItem, Timelined {
}
}
}
loopb:
for (final Tag t : tags) {
if (t instanceof ShapeTag) {
int characterID = 0;
@@ -1884,6 +1867,50 @@ public final class SWF implements TreeItem, Timelined {
return ret;
}
//TODO: implement morphshape export. How to handle 65536 frames?
public static List<File> exportMorphShapes(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, final MorphshapeExportMode mode) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
return ret;
}
File foutdir = new File(outdir);
if (!foutdir.exists()) {
if (!foutdir.mkdirs()) {
if (!foutdir.exists()) {
throw new IOException("Cannot create directory " + outdir);
}
}
}
for (final Tag t : tags) {
if (t instanceof MorphShapeTag) {
int characterID = 0;
if (t instanceof CharacterTag) {
characterID = ((CharacterTag) t).getCharacterId();
}
String ext = "svg";
final File file = new File(outdir + File.separator + characterID + "." + ext);
new RetryTask(new RunnableIOEx() {
@Override
public void run() throws IOException {
MorphShapeTag mst = (MorphShapeTag) t;
switch (mode) {
case SVG:
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(Utf8Helper.getBytes(mst.toSVG()));
}
break;
}
}
}, handler).run();
ret.add(file);
}
}
return ret;
}
public static List<File> exportBinaryData(AbortRetryIgnoreHandler handler, String outdir, List<Tag> tags, BinaryDataExportMode mode) throws IOException {
List<File> ret = new ArrayList<>();
if (tags.isEmpty()) {
@@ -1897,7 +1924,7 @@ public final class SWF implements TreeItem, Timelined {
}
}
}
loopb:
for (final Tag t : tags) {
if (t instanceof DefineBinaryDataTag) {
int characterID = ((DefineBinaryDataTag) t).getCharacterId();
@@ -1964,6 +1991,10 @@ public final class SWF implements TreeItem, Timelined {
exportShapes(handler, outdir, tags, mode);
}
public void exportMorphShapes(AbortRetryIgnoreHandler handler, String outdir, MorphshapeExportMode mode) throws IOException {
exportMorphShapes(handler, outdir, tags, mode);
}
public void exportBinaryData(AbortRetryIgnoreHandler handler, String outdir, BinaryDataExportMode mode) throws IOException {
exportBinaryData(handler, outdir, tags, mode);
}
@@ -46,7 +46,7 @@ import java.util.List;
*
* @author JPEXS
*/
public class BitmapExporter extends ShapeExporterBase implements IShapeExporter {
public class BitmapExporter extends ShapeExporterBase {
private SerializableImage image;
private Graphics2D graphics;
@@ -94,7 +94,7 @@ public class BitmapExporter extends ShapeExporterBase implements IShapeExporter
}
@Override
public void endShape(double xMin, double yMin, double xMax, double yMax) {
public void endShape() {
}
@Override
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
/**
*
* @author JPEXS
*/
public class CurvedMorphEdge extends StraightMorphEdge implements IMorphEdge {
private final PointInt control;
private final PointInt controlEnd;
CurvedMorphEdge(PointInt from, PointInt control, PointInt to,
PointInt fromEnd, PointInt controlEnd, PointInt toEnd, int lineStyleIdx, int fillStyleIdx) {
super(from, to, fromEnd, toEnd, lineStyleIdx, fillStyleIdx);
this.control = control;
this.controlEnd = controlEnd;
}
public PointInt getControl() {
return control;
}
public PointInt getControlEnd() {
return controlEnd;
}
@Override
public IMorphEdge reverseWithNewFillStyle(int newFillStyleIdx) {
return new CurvedMorphEdge(to, control, from, toEnd, controlEnd, fromEnd, lineStyleIdx, newFillStyleIdx);
}
}
@@ -0,0 +1,150 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.SHAPE;
/**
*
* @author JPEXS, Claus Wahlers
*/
public abstract class DefaultSVGMorphShapeExporter extends MorphShapeExporterBase {
protected static final String DRAW_COMMAND_L = "L";
protected static final String DRAW_COMMAND_Q = "Q";
protected String currentDrawCommand = "";
protected String pathData;
protected String pathDataEnd;
public DefaultSVGMorphShapeExporter(SHAPE shape, SHAPE endShape, ColorTransform colorTransform) {
super(shape, endShape, colorTransform);
}
@Override
public void beginShape() {
}
@Override
public void endShape() {
}
@Override
public void beginFills() {
}
@Override
public void endFills() {
}
@Override
public void beginLines() {
}
@Override
public void endLines() {
finalizePath();
}
@Override
public void beginFill(RGB color) {
finalizePath();
}
@Override
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
finalizePath();
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
}
@Override
public void endFill() {
finalizePath();
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) {
finalizePath();
}
@Override
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
}
@Override
public void moveTo(double x, double y, double x2, double y2) {
currentDrawCommand = "";
pathData += "M"
+ roundPixels20(x / SWF.unitDivisor) + " "
+ roundPixels20(y / SWF.unitDivisor) + " ";
pathDataEnd += "M"
+ roundPixels20(x2 / SWF.unitDivisor) + " "
+ roundPixels20(y2 / SWF.unitDivisor) + " ";
}
@Override
public void lineTo(double x, double y, double x2, double y2) {
if (!currentDrawCommand.equals(DRAW_COMMAND_L)) {
currentDrawCommand = DRAW_COMMAND_L;
pathData += "L";
pathDataEnd += "L";
}
pathData += roundPixels20(x / SWF.unitDivisor) + " "
+ roundPixels20(y / SWF.unitDivisor) + " ";
pathDataEnd += roundPixels20(x2 / SWF.unitDivisor) + " "
+ roundPixels20(y2 / SWF.unitDivisor) + " ";
}
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY, double controlX2, double controlY2, double anchorX2, double anchorY2) {
if (!currentDrawCommand.equals(DRAW_COMMAND_Q)) {
currentDrawCommand = DRAW_COMMAND_Q;
pathData += "Q";
pathDataEnd += "Q";
}
pathData += roundPixels20(controlX / SWF.unitDivisor) + " "
+ roundPixels20(controlY / SWF.unitDivisor) + " "
+ roundPixels20(anchorX / SWF.unitDivisor) + " "
+ roundPixels20(anchorY / SWF.unitDivisor) + " ";
pathDataEnd += roundPixels20(controlX2 / SWF.unitDivisor) + " "
+ roundPixels20(controlY2 / SWF.unitDivisor) + " "
+ roundPixels20(anchorX2 / SWF.unitDivisor) + " "
+ roundPixels20(anchorY2 / SWF.unitDivisor) + " ";
}
protected void finalizePath() {
pathData = "";
pathDataEnd = "";
currentDrawCommand = "";
}
protected double roundPixels20(double pixels) {
return Math.round(pixels * 100) / 100.0;
}
protected double roundPixels400(double pixels) {
return Math.round(pixels * 10000) / 10000.0;
}
}
@@ -26,7 +26,7 @@ import com.jpexs.decompiler.flash.types.SHAPE;
*
* @author JPEXS, Claus Wahlers
*/
public class DefaultSVGShapeExporter extends ShapeExporterBase implements IShapeExporter {
public abstract class DefaultSVGShapeExporter extends ShapeExporterBase {
protected static final String DRAW_COMMAND_L = "L";
protected static final String DRAW_COMMAND_Q = "Q";
@@ -43,7 +43,7 @@ public class DefaultSVGShapeExporter extends ShapeExporterBase implements IShape
}
@Override
public void endShape(double xMin, double yMin, double xMax, double yMax) {
public void endShape() {
}
@Override
@@ -102,7 +102,7 @@ public class DefaultSVGShapeExporter extends ShapeExporterBase implements IShape
@Override
public void lineTo(double x, double y) {
if (currentDrawCommand != DRAW_COMMAND_L) {
if (!currentDrawCommand.equals(DRAW_COMMAND_L)) {
currentDrawCommand = DRAW_COMMAND_L;
pathData += "L";
}
@@ -112,7 +112,7 @@ public class DefaultSVGShapeExporter extends ShapeExporterBase implements IShape
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
if (currentDrawCommand != DRAW_COMMAND_Q) {
if (!currentDrawCommand.equals(DRAW_COMMAND_Q)) {
currentDrawCommand = DRAW_COMMAND_Q;
pathData += "Q";
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
/**
*
* @author JPEXS
*/
public interface IMorphEdge {
public PointInt getFrom();
public PointInt getTo();
public PointInt getFromEnd();
public PointInt getToEnd();
public int getLineStyleIdx();
public int getFillStyleIdx();
public IMorphEdge reverseWithNewFillStyle(int newFillStyleIdx);
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.RGB;
/**
*
* @author JPEXS
*/
public interface IMorphShapeExporter {
public void beginShape();
public void endShape();
public void beginFills();
public void endFills();
public void beginLines();
public void endLines();
public void beginFill(RGB color);
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio);
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform);
public void endFill();
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit);
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio);
public void moveTo(double x, double y, double x2, double y2);
public void lineTo(double x, double y, double x2, double y2);
public void curveTo(double controlX, double controlY, double anchorX, double anchorY, double controlX2, double controlY2, double anchorX2, double anchorY2);
}
@@ -28,7 +28,7 @@ public interface IShapeExporter {
public void beginShape();
public void endShape(double xMin, double yMin, double xMax, double yMax);
public void endShape();
public void beginFills();
@@ -0,0 +1,546 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FOCALGRADIENT;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
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 java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author JPEXS, Claus Wahlers
*/
public abstract class MorphShapeExporterBase implements IMorphShapeExporter {
protected final SHAPE shape;
protected final SHAPE shapeEnd;
protected List<FILLSTYLE> _fillStyles;
protected List<LINESTYLE> _lineStyles;
protected List<FILLSTYLE> _fillStylesEnd;
protected List<LINESTYLE> _lineStylesEnd;
protected List<Map<Integer, List<IMorphEdge>>> _fillEdgeMaps;
protected List<Map<Integer, List<IMorphEdge>>> _lineEdgeMaps;
private boolean edgeMapsCreated;
protected ColorTransform colorTransform;
public MorphShapeExporterBase(SHAPE shape, SHAPE endShape, ColorTransform colorTransform) {
this.shape = shape;
this.shapeEnd = endShape;
this.colorTransform = colorTransform;
_fillStyles = new ArrayList<>();
_lineStyles = new ArrayList<>();
if (shape instanceof SHAPEWITHSTYLE) {
SHAPEWITHSTYLE shapeWithStyle = (SHAPEWITHSTYLE) shape;
_fillStyles.addAll(Arrays.asList(shapeWithStyle.fillStyles.fillStyles));
_lineStyles.addAll(Arrays.asList(shapeWithStyle.lineStyles.lineStyles));
}
_fillStylesEnd = new ArrayList<>();
_lineStylesEnd = new ArrayList<>();
if (endShape instanceof SHAPEWITHSTYLE) {
SHAPEWITHSTYLE shapeWithStyle = (SHAPEWITHSTYLE) endShape;
_fillStylesEnd.addAll(Arrays.asList(shapeWithStyle.fillStyles.fillStyles));
_lineStylesEnd.addAll(Arrays.asList(shapeWithStyle.lineStyles.lineStyles));
}
}
public void export() {
// Create edge maps
_fillEdgeMaps = new ArrayList<>();
_lineEdgeMaps = new ArrayList<>();
createEdgeMaps(_fillStyles, _lineStyles, _fillEdgeMaps, _lineEdgeMaps);
// Let the doc handler know that a shape export starts
beginShape();
// Export fills and strokes for each group separately
for (int i = 0; i < _lineEdgeMaps.size(); i++) {
// Export fills first
exportFillPath(i);
// Export strokes last
exportLinePath(i);
}
// Let the doc handler know that we're done exporting a shape
endShape();
}
protected void createEdgeMaps(List<FILLSTYLE> fillStyles, List<LINESTYLE> lineStyles,
List<Map<Integer, List<IMorphEdge>>> fillEdgeMaps, List<Map<Integer, List<IMorphEdge>>> lineEdgeMaps) {
if (!edgeMapsCreated) {
int xPos = 0;
int yPos = 0;
int xPosEnd = 0;
int yPosEnd = 0;
int fillStyleIdxOffset = 0;
int lineStyleIdxOffset = 0;
int currentFillStyleIdx0 = 0;
int currentFillStyleIdx1 = 0;
int currentLineStyleIdx = 0;
List<IMorphEdge> subPath = new ArrayList<>();
Map<Integer, List<IMorphEdge>> currentFillEdgeMap = new HashMap<>();
Map<Integer, List<IMorphEdge>> currentLineEdgeMap = new HashMap<>();
List<SHAPERECORD> records = shape.shapeRecords;
List<SHAPERECORD> recordsEnd = shapeEnd.shapeRecords;
if (records.size() != recordsEnd.size()) {
throw new Error("Begin and end shaperecord list length should be the same.");
}
for (int i = 0; i < records.size(); i++) {
SHAPERECORD shapeRecord = records.get(i);
SHAPERECORD shapeRecordEnd = recordsEnd.get(i);
if ((shapeRecord instanceof StyleChangeRecord && !(shapeRecordEnd instanceof StyleChangeRecord)) ||
(shapeRecord instanceof StraightEdgeRecord && !(shapeRecordEnd instanceof StraightEdgeRecord)) ||
(shapeRecord instanceof CurvedEdgeRecord && !(shapeRecordEnd instanceof CurvedEdgeRecord)) ||
(shapeRecord instanceof EndShapeRecord && !(shapeRecordEnd instanceof EndShapeRecord))) {
throw new Error("Begin and end shaperecord should have the same type.");
}
if (shapeRecord instanceof StyleChangeRecord) {
StyleChangeRecord styleChangeRecord = (StyleChangeRecord) shapeRecord;
if (styleChangeRecord.stateLineStyle || styleChangeRecord.stateFillStyle0 || styleChangeRecord.stateFillStyle1) {
processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1, currentFillEdgeMap, currentLineEdgeMap);
subPath = new ArrayList<>();
}
if (styleChangeRecord.stateNewStyles) {
fillStyleIdxOffset = fillStyles.size();
lineStyleIdxOffset = lineStyles.size();
appendFillStyles(fillStyles, styleChangeRecord.fillStyles.fillStyles);
appendLineStyles(lineStyles, styleChangeRecord.lineStyles.lineStyles);
}
// Check if all styles are reset to 0.
// This (probably) means that a new group starts with the next record
if (styleChangeRecord.stateLineStyle && styleChangeRecord.lineStyle == 0
&& styleChangeRecord.stateFillStyle0 && styleChangeRecord.fillStyle0 == 0
&& styleChangeRecord.stateFillStyle1 && styleChangeRecord.fillStyle1 == 0) {
// do not clean the edges for morphshapes
//cleanEdgeMap(currentFillEdgeMap);
//cleanEdgeMap(currentLineEdgeMap);
fillEdgeMaps.add(currentFillEdgeMap);
lineEdgeMaps.add(currentLineEdgeMap);
currentFillEdgeMap = new HashMap<>();
currentLineEdgeMap = new HashMap<>();
currentLineStyleIdx = 0;
currentFillStyleIdx0 = 0;
currentFillStyleIdx1 = 0;
} else {
if (styleChangeRecord.stateLineStyle) {
currentLineStyleIdx = styleChangeRecord.lineStyle;
if (currentLineStyleIdx > 0) {
currentLineStyleIdx += lineStyleIdxOffset;
}
}
if (styleChangeRecord.stateFillStyle0) {
currentFillStyleIdx0 = styleChangeRecord.fillStyle0;
if (currentFillStyleIdx0 > 0) {
currentFillStyleIdx0 += fillStyleIdxOffset;
}
}
if (styleChangeRecord.stateFillStyle1) {
currentFillStyleIdx1 = styleChangeRecord.fillStyle1;
if (currentFillStyleIdx1 > 0) {
currentFillStyleIdx1 += fillStyleIdxOffset;
}
}
}
if (styleChangeRecord.stateMoveTo) {
xPos = styleChangeRecord.moveDeltaX;
yPos = styleChangeRecord.moveDeltaY;
}
StyleChangeRecord styleChangeRecordEnd = (StyleChangeRecord) shapeRecordEnd;
if (styleChangeRecordEnd.stateMoveTo) {
xPosEnd = styleChangeRecordEnd.moveDeltaX;
yPosEnd = styleChangeRecordEnd.moveDeltaY;
}
} else if (shapeRecord instanceof StraightEdgeRecord) {
StraightEdgeRecord straightEdgeRecord = (StraightEdgeRecord) shapeRecord;
PointInt from = new PointInt(xPos, yPos);
if (straightEdgeRecord.generalLineFlag) {
xPos += straightEdgeRecord.deltaX;
yPos += straightEdgeRecord.deltaY;
} else {
if (straightEdgeRecord.vertLineFlag) {
yPos += straightEdgeRecord.deltaY;
} else {
xPos += straightEdgeRecord.deltaX;
}
}
PointInt to = new PointInt(xPos, yPos);
StraightEdgeRecord straightEdgeRecordEnd = (StraightEdgeRecord) shapeRecordEnd;
PointInt fromEnd = new PointInt(xPosEnd, yPosEnd);
if (straightEdgeRecordEnd.generalLineFlag) {
xPosEnd += straightEdgeRecordEnd.deltaX;
yPosEnd += straightEdgeRecordEnd.deltaY;
} else {
if (straightEdgeRecordEnd.vertLineFlag) {
yPosEnd += straightEdgeRecordEnd.deltaY;
} else {
xPosEnd += straightEdgeRecordEnd.deltaX;
}
}
PointInt toEnd = new PointInt(xPosEnd, yPosEnd);
subPath.add(new StraightMorphEdge(from, to, fromEnd, toEnd, currentLineStyleIdx, currentFillStyleIdx1));
} else if (shapeRecord instanceof CurvedEdgeRecord) {
CurvedEdgeRecord curvedEdgeRecord = (CurvedEdgeRecord) shapeRecord;
PointInt from = new PointInt(xPos, yPos);
int xPosControl = xPos + curvedEdgeRecord.controlDeltaX;
int yPosControl = yPos + curvedEdgeRecord.controlDeltaY;
xPos = xPosControl + curvedEdgeRecord.anchorDeltaX;
yPos = yPosControl + curvedEdgeRecord.anchorDeltaY;
PointInt control = new PointInt(xPosControl, yPosControl);
PointInt to = new PointInt(xPos, yPos);
CurvedEdgeRecord curvedEdgeRecordEnd = (CurvedEdgeRecord) shapeRecordEnd;
PointInt fromEnd = new PointInt(xPosEnd, yPosEnd);
int xPosControlEnd = xPosEnd + curvedEdgeRecordEnd.controlDeltaX;
int yPosControlEnd = yPosEnd + curvedEdgeRecordEnd.controlDeltaY;
xPosEnd = xPosControlEnd + curvedEdgeRecordEnd.anchorDeltaX;
yPosEnd = yPosControlEnd + curvedEdgeRecordEnd.anchorDeltaY;
PointInt controlEnd = new PointInt(xPosControlEnd, yPosControlEnd);
PointInt toEnd = new PointInt(xPosEnd, yPosEnd);
subPath.add(new CurvedMorphEdge(from, control, to, fromEnd, controlEnd, toEnd, currentLineStyleIdx, currentFillStyleIdx1));
} else if (shapeRecord instanceof EndShapeRecord) {
// We're done. Process the last subpath, if any
processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1, currentFillEdgeMap, currentLineEdgeMap);
// do not clean the edges for morphshapes
//cleanEdgeMap(currentFillEdgeMap);
//cleanEdgeMap(currentLineEdgeMap);
fillEdgeMaps.add(currentFillEdgeMap);
lineEdgeMaps.add(currentLineEdgeMap);
}
}
edgeMapsCreated = true;
}
}
protected void processSubPath(List<IMorphEdge> subPath, int lineStyleIdx, int fillStyleIdx0, int fillStyleIdx1,
Map<Integer, List<IMorphEdge>> currentFillEdgeMap, Map<Integer, List<IMorphEdge>> currentLineEdgeMap) {
List<IMorphEdge> path;
if (fillStyleIdx0 != 0) {
path = currentFillEdgeMap.get(fillStyleIdx0);
if (path == null) {
path = new ArrayList<>();
currentFillEdgeMap.put(fillStyleIdx0, path);
}
for (int j = subPath.size() - 1; j >= 0; j--) {
path.add(subPath.get(j).reverseWithNewFillStyle(fillStyleIdx0));
}
}
if (fillStyleIdx1 != 0) {
path = currentFillEdgeMap.get(fillStyleIdx1);
if (path == null) {
path = new ArrayList<>();
currentFillEdgeMap.put(fillStyleIdx1, path);
}
appendEdges(path, subPath);
}
if (lineStyleIdx != 0) {
path = currentLineEdgeMap.get(lineStyleIdx);
if (path == null) {
path = new ArrayList<>();
currentLineEdgeMap.put(lineStyleIdx, path);
}
appendEdges(path, subPath);
}
}
protected void exportFillPath(int groupIndex) {
List<IMorphEdge> path = createPathFromEdgeMap(_fillEdgeMaps.get(groupIndex));
PointInt pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
int fillStyleIdx = Integer.MAX_VALUE;
if (path.size() > 0) {
beginFills();
for (int i = 0; i < path.size(); i++) {
IMorphEdge e = path.get(i);
if (fillStyleIdx != e.getFillStyleIdx()) {
if (fillStyleIdx != Integer.MAX_VALUE) {
endFill();
}
fillStyleIdx = e.getFillStyleIdx();
pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
try {
Matrix matrix;
FILLSTYLE fillStyle = _fillStyles.get(fillStyleIdx - 1);
switch (fillStyle.fillStyleType) {
case FILLSTYLE.SOLID:
// Solid fill
beginFill(colorTransform.apply(fillStyle.color));
break;
case FILLSTYLE.LINEAR_GRADIENT:
case FILLSTYLE.RADIAL_GRADIENT:
case FILLSTYLE.FOCAL_RADIAL_GRADIENT:
// Gradient fill
matrix = new Matrix(fillStyle.gradientMatrix);
beginGradientFill(
fillStyle.fillStyleType,
colorTransform.apply(fillStyle.gradient.gradientRecords),
matrix,
fillStyle.gradient.spreadMode,
fillStyle.gradient.interpolationMode,
(fillStyle.gradient instanceof FOCALGRADIENT) ? ((FOCALGRADIENT) fillStyle.gradient).focalPoint : 0
);
break;
case FILLSTYLE.REPEATING_BITMAP:
case FILLSTYLE.CLIPPED_BITMAP:
case FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP:
case FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP:
// Bitmap fill
matrix = new Matrix(fillStyle.bitmapMatrix);
beginBitmapFill(
fillStyle.bitmapId,
matrix,
(fillStyle.fillStyleType == FILLSTYLE.REPEATING_BITMAP || fillStyle.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP),
(fillStyle.fillStyleType == FILLSTYLE.REPEATING_BITMAP || fillStyle.fillStyleType == FILLSTYLE.CLIPPED_BITMAP),
colorTransform
);
break;
}
} catch (Exception ex) {
// Font shapes define no fillstyles per se, but do reference fillstyle index 1,
// which represents the font color. We just report null in this case.
beginFill(null);
}
}
if (!pos.equals(e.getFrom())) {
moveTo(e.getFrom().x, e.getFrom().y, e.getFromEnd().x, e.getFromEnd().y);
}
if (e instanceof CurvedMorphEdge) {
CurvedMorphEdge c = (CurvedMorphEdge) e;
curveTo(c.getControl().x, c.getControl().y, c.to.x, c.to.y, c.getControlEnd().x, c.getControlEnd().y, c.toEnd.x, c.toEnd.y);
} else {
lineTo(e.getTo().x, e.getTo().y, e.getToEnd().x, e.getToEnd().y);
}
pos = e.getTo();
}
if (fillStyleIdx != Integer.MAX_VALUE) {
endFill();
}
endFills();
}
}
protected void exportLinePath(int groupIndex) {
List<IMorphEdge> path = createPathFromEdgeMap(_lineEdgeMaps.get(groupIndex));
PointInt pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
int lineStyleIdx = Integer.MAX_VALUE;
LINESTYLE lineStyle;
if (path.size() > 0) {
beginLines();
for (int i = 0; i < path.size(); i++) {
IMorphEdge e = path.get(i);
if (lineStyleIdx != e.getLineStyleIdx()) {
lineStyleIdx = e.getLineStyleIdx();
pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
try {
lineStyle = _lineStyles.get(lineStyleIdx - 1);
} catch (Exception ex) {
lineStyle = null;
}
if (lineStyle != null) {
String scaleMode = "NORMAL";
boolean pixelHintingFlag = false;
int startCapStyle = LINESTYLE2.ROUND_CAP;
int endCapStyle = LINESTYLE2.ROUND_CAP;
int joinStyle = LINESTYLE2.ROUND_JOIN;
int miterLimitFactor = 3;
boolean hasFillFlag = false;
if (lineStyle instanceof LINESTYLE2) {
LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle;
if (lineStyle2.noHScaleFlag && lineStyle2.noVScaleFlag) {
scaleMode = "NONE";
} else if (lineStyle2.noHScaleFlag) {
scaleMode = "HORIZONTAL";
} else if (lineStyle2.noVScaleFlag) {
scaleMode = "VERTICAL";
}
pixelHintingFlag = lineStyle2.pixelHintingFlag;
startCapStyle = lineStyle2.startCapStyle;
endCapStyle = lineStyle2.endCapStyle;
joinStyle = lineStyle2.joinStyle;
miterLimitFactor = lineStyle2.miterLimitFactor;
hasFillFlag = lineStyle2.hasFillFlag;
}
lineStyle(
lineStyle.width,
colorTransform.apply(lineStyle.color),
pixelHintingFlag,
scaleMode,
startCapStyle,
endCapStyle,
joinStyle,
miterLimitFactor);
if (hasFillFlag) {
LINESTYLE2 lineStyle2 = (LINESTYLE2) lineStyle;
FILLSTYLE fillStyle = lineStyle2.fillType;
switch (fillStyle.fillStyleType) {
case FILLSTYLE.LINEAR_GRADIENT:
case FILLSTYLE.RADIAL_GRADIENT:
case FILLSTYLE.FOCAL_RADIAL_GRADIENT:
// Gradient fill
Matrix matrix = new Matrix(fillStyle.gradientMatrix);
lineGradientStyle(
fillStyle.fillStyleType,
fillStyle.gradient.gradientRecords,
matrix,
fillStyle.gradient.spreadMode,
fillStyle.gradient.interpolationMode,
(fillStyle.gradient instanceof FOCALGRADIENT) ? ((FOCALGRADIENT) fillStyle.gradient).focalPoint : 0
);
break;
}
}
} else {
// We should never get here
lineStyle(1, new RGB(Color.BLACK), false, "NORMAL", 0, 0, 0, 3);
}
}
if (!e.getFrom().equals(pos)) {
moveTo(e.getFrom().x, e.getFrom().y, e.getFromEnd().x, e.getFromEnd().y);
}
if (e instanceof CurvedMorphEdge) {
CurvedMorphEdge c = (CurvedMorphEdge) e;
curveTo(c.getControl().x, c.getControl().y, c.to.x, c.to.y, c.getControlEnd().x, c.getControlEnd().y, c.toEnd.x, c.toEnd.y);
} else {
lineTo(e.getTo().x, e.getTo().y, e.getToEnd().x, e.getToEnd().y);
}
pos = e.getTo();
}
endLines();
}
}
protected List<IMorphEdge> createPathFromEdgeMap(Map<Integer, List<IMorphEdge>> edgeMap) {
List<IMorphEdge> newPath = new ArrayList<>();
List<Integer> styleIdxArray = new ArrayList<>();
for (Integer styleIdx : edgeMap.keySet()) {
styleIdxArray.add(styleIdx);
}
Collections.sort(styleIdxArray);
for (int i = 0; i < styleIdxArray.size(); i++) {
appendEdges(newPath, edgeMap.get(styleIdxArray.get(i)));
}
return newPath;
}
protected void cleanEdgeMap(Map<Integer, List<IMorphEdge>> edgeMap) {
for (Integer styleIdx : edgeMap.keySet()) {
List<IMorphEdge> subPath = edgeMap.get(styleIdx);
if (subPath != null && subPath.size() > 0) {
int idx;
IMorphEdge prevEdge = null;
List<IMorphEdge> tmpPath = new ArrayList<>();
Map<String, List<IMorphEdge>> coordMap = createCoordMap(subPath);
while (subPath.size() > 0) {
idx = 0;
while (idx < subPath.size()) {
if (prevEdge == null || prevEdge.getTo().equals(subPath.get(idx).getFrom())) {
IMorphEdge edge = subPath.remove(idx);
tmpPath.add(edge);
removeEdgeFromCoordMap(coordMap, edge);
prevEdge = edge;
} else {
IMorphEdge edge = findNextEdgeInCoordMap(coordMap, prevEdge);
if (edge != null) {
idx = subPath.indexOf(edge);
} else {
idx = 0;
prevEdge = null;
}
}
}
}
edgeMap.put(styleIdx, tmpPath);
}
}
}
protected Map<String, List<IMorphEdge>> createCoordMap(List<IMorphEdge> path) {
Map<String, List<IMorphEdge>> coordMap = new HashMap<>();
for (int i = 0; i < path.size(); i++) {
PointInt from = path.get(i).getFrom();
String key = from.x + "_" + from.y;
List<IMorphEdge> coordMapArray = coordMap.get(key);
if (coordMapArray == null) {
List<IMorphEdge> list = new ArrayList<>();
list.add(path.get(i));
coordMap.put(key, list);
} else {
coordMapArray.add(path.get(i));
}
}
return coordMap;
}
protected void removeEdgeFromCoordMap(Map<String, List<IMorphEdge>> coordMap, IMorphEdge edge) {
String key = edge.getFrom().x + "_" + edge.getFrom().y;
List<IMorphEdge> coordMapArray = coordMap.get(key);
if (coordMapArray != null) {
if (coordMapArray.size() == 1) {
coordMap.remove(key);
} else {
int i = coordMapArray.indexOf(edge);
if (i > -1) {
coordMapArray.remove(i);
}
}
}
}
protected IMorphEdge findNextEdgeInCoordMap(Map<String, List<IMorphEdge>> coordMap, IMorphEdge edge) {
String key = edge.getTo().x + "_" + edge.getTo().y;
List<IMorphEdge> coordMapArray = coordMap.get(key);
if (coordMapArray != null && coordMapArray.size() > 0) {
return coordMapArray.get(0);
}
return null;
}
protected void appendFillStyles(List<FILLSTYLE> v1, FILLSTYLE[] v2) {
v1.addAll(Arrays.asList(v2));
}
protected void appendLineStyles(List<LINESTYLE> v1, LINESTYLE[] v2) {
v1.addAll(Arrays.asList(v2));
}
protected void appendEdges(List<IMorphEdge> v1, List<IMorphEdge> v2) {
for (int i = 0; i < v2.size(); i++) {
v1.add(v2.get(i));
}
}
}
@@ -56,7 +56,7 @@ public class PathExporter extends ShapeExporterBase {
}
@Override
public void endShape(double xMin, double yMin, double xMax, double yMax) {
public void endShape() {
}
@@ -0,0 +1,353 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.SerializableImage;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.xml.bind.DatatypeConverter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
/**
*
* @author JPEXS, Claus Wahlers
*/
public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter {
protected static final String sNamespace = "http://www.w3.org/2000/svg";
protected static final String xlinkNamespace = "http://www.w3.org/1999/xlink";
protected Document _svg;
protected Element _svgDefs;
protected Element _svgG;
protected Element path;
protected List<Element> gradients;
protected int lastPatternId;
private final SWF swf;
private double maxLineWidth;
private final ExportRectangle bounds;
public SVGMorphShapeExporter(SWF swf, SHAPE shape, SHAPE endShape, ExportRectangle bounds, ColorTransform colorTransform) {
super(shape, endShape, colorTransform);
this.swf = swf;
this.bounds = bounds;
}
public String getSVG() {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
StringWriter writer = new StringWriter();
try {
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(_svg);
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
} catch (TransformerException ex) {
Logger.getLogger(SVGShapeExporter.class.getName()).log(Level.SEVERE, null, ex);
}
return writer.toString();
}
@Override
public void beginShape() {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
DOMImplementation impl = docBuilder.getDOMImplementation();
DocumentType svgDocType = impl.createDocumentType("svg", "-//W3C//DTD SVG 1.0//EN",
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
_svg = impl.createDocument(sNamespace, "svg", svgDocType);
Element svgRoot = _svg.getDocumentElement();
svgRoot.setAttribute("xmlns:xlink", xlinkNamespace);
_svgDefs = _svg.createElement("defs");
svgRoot.appendChild(_svgDefs);
_svgG = _svg.createElement("g");
_svgG.setAttribute("transform", "matrix(1, 0, 0, 1, "
+ roundPixels20(-bounds.xMin / (double) SWF.unitDivisor) + ", " + roundPixels20(-bounds.yMin / (double) SWF.unitDivisor) + ")");
svgRoot.appendChild(_svgG);
} catch (ParserConfigurationException ex) {
Logger.getLogger(SVGShapeExporter.class.getName()).log(Level.SEVERE, null, ex);
}
gradients = new ArrayList<>();
}
@Override
public void beginFill(RGB color) {
if (color == null) {
color = new RGB(Color.BLACK);
}
finalizePath();
path.setAttribute("stroke", "none");
path.setAttribute("fill", color.toHexRGB());
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
path.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat()));
}
}
}
@Override
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
finalizePath();
Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT)
? _svg.createElement("linearGradient")
: _svg.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio);
int id = gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = gradients.size();
gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "none");
path.setAttribute("fill", "url(#gradient" + id + ")");
_svgDefs.appendChild(gradient);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = null;
for (Tag t : swf.tags) {
if (t instanceof ImageTag) {
ImageTag i = (ImageTag) t;
if (i.getCharacterId() == bitmapId) {
image = i;
break;
}
}
}
if (image != null) {
SerializableImage img = image.getImage();
if (img != null) {
colorTransform.apply(img);
int width = img.getWidth();
int height = img.getHeight();
lastPatternId++;
String patternId = "PatternID_" + lastPatternId;
String format = image.getImageFormat();
InputStream imageStream = image.getImageData();
byte[] imageData;
if (imageStream != null) {
imageData = Helper.readStream(image.getImageData());
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(img.getBufferedImage(), format.toUpperCase(Locale.ENGLISH), baos);
} catch (IOException ex) {
}
imageData = baos.toByteArray();
}
String base64ImgData = DatatypeConverter.printBase64Binary(imageData);
path.setAttribute("style", "fill:url(#" + patternId + ")");
Element pattern = _svg.createElement("pattern");
pattern.setAttribute("id", patternId);
pattern.setAttribute("patternUnits", "userSpaceOnUse");
pattern.setAttribute("overflow", "visible");
pattern.setAttribute("width", "" + width);
pattern.setAttribute("height", "" + height);
pattern.setAttribute("viewBox", "0 0 " + width + " " + height);
if (matrix != null) {
double translateX = roundPixels400(matrix.translateX / SWF.unitDivisor);
double translateY = roundPixels400(matrix.translateY / SWF.unitDivisor);
double rotateSkew0 = roundPixels400(matrix.rotateSkew0 / SWF.unitDivisor);
double rotateSkew1 = roundPixels400(matrix.rotateSkew1 / SWF.unitDivisor);
double scaleX = roundPixels400(matrix.scaleX / SWF.unitDivisor);
double scaleY = roundPixels400(matrix.scaleY / SWF.unitDivisor);
pattern.setAttribute("patternTransform", "matrix(" + scaleX + ", " + rotateSkew0
+ ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")");
}
Element imageElement = _svg.createElement("image");
imageElement.setAttribute("width", "" + width);
imageElement.setAttribute("height", "" + height);
imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData);
pattern.appendChild(imageElement);
_svgG.appendChild(pattern);
}
}
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) {
finalizePath();
if (thickness > maxLineWidth) {
maxLineWidth = thickness;
}
thickness /= SWF.unitDivisor;
path.setAttribute("fill", "none");
path.setAttribute("stroke", color.toHexRGB());
path.setAttribute("stroke-width", Double.toString(thickness == 0 ? 1 : thickness));
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
path.setAttribute("stroke-opacity", Float.toString(colorA.getAlphaFloat()));
}
}
switch (startCaps) {
case LINESTYLE2.NO_CAP:
path.setAttribute("stroke-linecap", "butt");
break;
case LINESTYLE2.SQUARE_CAP:
path.setAttribute("stroke-linecap", "square");
break;
default:
path.setAttribute("stroke-linecap", "round");
break;
}
switch (joints) {
case LINESTYLE2.BEVEL_JOIN:
path.setAttribute("stroke-linejoin", "bevel");
break;
case LINESTYLE2.ROUND_JOIN:
path.setAttribute("stroke-linejoin", "round");
break;
default:
path.setAttribute("stroke-linejoin", "miter");
if (miterLimit >= 1 && miterLimit != 4) {
path.setAttribute("stroke-miterlimit", Integer.toString(miterLimit));
}
break;
}
}
@Override
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
path.removeAttribute("stroke-opacity");
Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT)
? _svg.createElement("linearGradient")
: _svg.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio);
int id = gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = gradients.size();
gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "url(#gradient" + id + ")");
path.setAttribute("fill", "none");
_svgDefs.appendChild(gradient);
}
@Override
protected void finalizePath() {
if (path != null && !"".equals(pathData)) {
path.setAttribute("d", pathData.trim());
Element animatePath = _svg.createElement("animate");
animatePath.setAttribute("dur", "2s"); // todo
animatePath.setAttribute("repeatCount", "indefinite");
animatePath.setAttribute("attributeName", "d");
animatePath.setAttribute("values", pathData.trim() + ";" + pathDataEnd.trim());
path.appendChild(animatePath);
_svgG.appendChild(path);
}
path = _svg.createElement("path");
super.finalizePath();
}
protected void populateGradientElement(Element gradient, int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
if (type == FILLSTYLE.LINEAR_GRADIENT) {
gradient.setAttribute("x1", "-819.2");
gradient.setAttribute("x2", "819.2");
} else {
gradient.setAttribute("r", "819.2");
gradient.setAttribute("cx", "0");
gradient.setAttribute("cy", "0");
if (focalPointRatio != 0) {
gradient.setAttribute("fx", Double.toString(819.2 * focalPointRatio));
gradient.setAttribute("fy", "0");
}
}
switch (spreadMethod) {
case GRADIENT.SPREAD_PAD_MODE:
gradient.setAttribute("spreadMethod", "pad");
break;
case GRADIENT.SPREAD_REFLECT_MODE:
gradient.setAttribute("spreadMethod", "reflect");
break;
case GRADIENT.SPREAD_REPEAT_MODE:
gradient.setAttribute("spreadMethod", "repeat");
break;
}
if (interpolationMethod == GRADIENT.INTERPOLATION_LINEAR_RGB_MODE) {
gradient.setAttribute("color-interpolation", "linearRGB");
}
if (matrix != null) {
double translateX = roundPixels400(matrix.translateX / SWF.unitDivisor);
double translateY = roundPixels400(matrix.translateY / SWF.unitDivisor);
double rotateSkew0 = roundPixels400(matrix.rotateSkew0);
double rotateSkew1 = roundPixels400(matrix.rotateSkew1);
double scaleX = roundPixels400(matrix.scaleX);
double scaleY = roundPixels400(matrix.scaleY);
gradient.setAttribute("gradientTransform", "matrix(" + scaleX + ", " + rotateSkew0
+ ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")");
}
for (int i = 0; i < gradientRecords.length; i++) {
GRADRECORD record = gradientRecords[i];
Element gradientEntry = _svg.createElement("stop");
gradientEntry.setAttribute("offset", Double.toString(record.ratio / 255.0));
RGB color = record.color;
//if(colors.get(i) != 0) {
gradientEntry.setAttribute("stop-color", color.toHexRGB());
//}
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
gradientEntry.setAttribute("stop-opacity", Float.toString(colorA.getAlphaFloat()));
}
}
gradient.appendChild(gradientEntry);
}
}
}
@@ -72,10 +72,12 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
protected int lastPatternId;
private final SWF swf;
private double maxLineWidth;
private final ExportRectangle bounds;
public SVGShapeExporter(SWF swf, SHAPE shape, ColorTransform colorTransform) {
public SVGShapeExporter(SWF swf, SHAPE shape, ExportRectangle bounds, ColorTransform colorTransform) {
super(shape, colorTransform);
this.swf = swf;
this.bounds = bounds;
}
public String getSVG() {
@@ -103,6 +105,8 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
DocumentType svgDocType = impl.createDocumentType("svg", "-//W3C//DTD SVG 1.0//EN",
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
_svg = impl.createDocument(sNamespace, "svg", svgDocType);
_svgG.setAttribute("transform", "matrix(1, 0, 0, 1, "
+ roundPixels20(-bounds.xMin / (double) SWF.unitDivisor) + ", " + roundPixels20(-bounds.yMin / (double) SWF.unitDivisor) + ")");
Element svgRoot = _svg.getDocumentElement();
svgRoot.setAttribute("xmlns:xlink", xlinkNamespace);
_svgDefs = _svg.createElement("defs");
@@ -115,16 +119,6 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
gradients = new ArrayList<>();
}
@Override
public void endShape(double xMin, double yMin, double xMax, double yMax) {
xMin /= SWF.unitDivisor;
yMin /= SWF.unitDivisor;
xMin -= maxLineWidth / SWF.unitDivisor;
yMin -= maxLineWidth / SWF.unitDivisor;
_svgG.setAttribute("transform", "matrix(1, 0, 0, 1, "
+ roundPixels20(-xMin) + ", " + roundPixels20(-yMin) + ")");
}
@Override
public void beginFill(RGB color) {
if (color == null) {
@@ -287,7 +281,7 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
@Override
protected void finalizePath() {
if (path != null && pathData != "") {
if (path != null && !"".equals(pathData)) {
path.setAttribute("d", pathData.trim());
_svgG.appendChild(path);
}
@@ -48,13 +48,8 @@ public abstract class ShapeExporterBase implements IShapeExporter {
protected List<FILLSTYLE> _fillStyles;
protected List<LINESTYLE> _lineStyles;
protected List<Map<Integer, List<IEdge>>> fillEdgeMaps;
protected List<Map<Integer, List<IEdge>>> lineEdgeMaps;
protected Map<Integer, List<IEdge>> currentFillEdgeMap;
protected Map<Integer, List<IEdge>> currentLineEdgeMap;
private int numGroups;
protected Map<String, List<IEdge>> coordMap;
private final ExportRectangle bounds = new ExportRectangle(Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Double.MIN_VALUE);
protected List<Map<Integer, List<IEdge>>> _fillEdgeMaps;
protected List<Map<Integer, List<IEdge>>> _lineEdgeMaps;
private boolean edgeMapsCreated;
protected ColorTransform colorTransform;
@@ -73,21 +68,25 @@ public abstract class ShapeExporterBase implements IShapeExporter {
public void export() {
// Create edge maps
createEdgeMaps();
_fillEdgeMaps = new ArrayList<>();
_lineEdgeMaps = new ArrayList<>();
createEdgeMaps(_fillStyles, _lineStyles, _fillEdgeMaps, _lineEdgeMaps);
// Let the doc handler know that a shape export starts
beginShape();
// Export fills and strokes for each group separately
for (int i = 0; i < numGroups; i++) {
for (int i = 0; i < _lineEdgeMaps.size(); i++) {
// Export fills first
exportFillPath(i);
// Export strokes last
exportLinePath(i);
}
// Let the doc handler know that we're done exporting a shape
endShape(bounds.xMin, bounds.yMin, bounds.xMax, bounds.yMax);
endShape();
}
protected void createEdgeMaps() {
protected void createEdgeMaps(List<FILLSTYLE> fillStyles, List<LINESTYLE> lineStyles,
List<Map<Integer, List<IEdge>>> fillEdgeMaps, List<Map<Integer, List<IEdge>>> lineEdgeMaps) {
if (!edgeMapsCreated) {
int xPos = 0;
int yPos = 0;
@@ -97,25 +96,22 @@ public abstract class ShapeExporterBase implements IShapeExporter {
int currentFillStyleIdx1 = 0;
int currentLineStyleIdx = 0;
List<IEdge> subPath = new ArrayList<>();
numGroups = 0;
fillEdgeMaps = new ArrayList<>();
lineEdgeMaps = new ArrayList<>();
currentFillEdgeMap = new HashMap<>();
currentLineEdgeMap = new HashMap<>();
Map<Integer, List<IEdge>> currentFillEdgeMap = new HashMap<>();
Map<Integer, List<IEdge>> currentLineEdgeMap = new HashMap<>();
List<SHAPERECORD> records = shape.shapeRecords;
for (int i = 0; i < records.size(); i++) {
SHAPERECORD shapeRecord = records.get(i);
if (shapeRecord instanceof StyleChangeRecord) {
StyleChangeRecord styleChangeRecord = (StyleChangeRecord) shapeRecord;
if (styleChangeRecord.stateLineStyle || styleChangeRecord.stateFillStyle0 || styleChangeRecord.stateFillStyle1) {
processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1);
processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1, currentFillEdgeMap, currentLineEdgeMap);
subPath = new ArrayList<>();
}
if (styleChangeRecord.stateNewStyles) {
fillStyleIdxOffset = _fillStyles.size();
lineStyleIdxOffset = _lineStyles.size();
appendFillStyles(_fillStyles, styleChangeRecord.fillStyles.fillStyles);
appendLineStyles(_lineStyles, styleChangeRecord.lineStyles.lineStyles);
fillStyleIdxOffset = fillStyles.size();
lineStyleIdxOffset = lineStyles.size();
appendFillStyles(fillStyles, styleChangeRecord.fillStyles.fillStyles);
appendLineStyles(lineStyles, styleChangeRecord.lineStyles.lineStyles);
}
// Check if all styles are reset to 0.
// This (probably) means that a new group starts with the next record
@@ -131,7 +127,6 @@ public abstract class ShapeExporterBase implements IShapeExporter {
currentLineStyleIdx = 0;
currentFillStyleIdx0 = 0;
currentFillStyleIdx1 = 0;
numGroups++;
} else {
if (styleChangeRecord.stateLineStyle) {
currentLineStyleIdx = styleChangeRecord.lineStyle;
@@ -183,19 +178,19 @@ public abstract class ShapeExporterBase implements IShapeExporter {
subPath.add(new CurvedEdge(from, control, to, currentLineStyleIdx, currentFillStyleIdx1));
} else if (shapeRecord instanceof EndShapeRecord) {
// We're done. Process the last subpath, if any
processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1);
processSubPath(subPath, currentLineStyleIdx, currentFillStyleIdx0, currentFillStyleIdx1, currentFillEdgeMap, currentLineEdgeMap);
cleanEdgeMap(currentFillEdgeMap);
cleanEdgeMap(currentLineEdgeMap);
fillEdgeMaps.add(currentFillEdgeMap);
lineEdgeMaps.add(currentLineEdgeMap);
numGroups++;
}
}
edgeMapsCreated = true;
}
}
protected void processSubPath(List<IEdge> subPath, int lineStyleIdx, int fillStyleIdx0, int fillStyleIdx1) {
protected void processSubPath(List<IEdge> subPath, int lineStyleIdx, int fillStyleIdx0, int fillStyleIdx1,
Map<Integer, List<IEdge>> currentFillEdgeMap, Map<Integer, List<IEdge>> currentLineEdgeMap) {
List<IEdge> path;
if (fillStyleIdx0 != 0) {
path = currentFillEdgeMap.get(fillStyleIdx0);
@@ -225,42 +220,14 @@ public abstract class ShapeExporterBase implements IShapeExporter {
}
}
private void calculateBound(IEdge edge) {
PointInt from = edge.getFrom();
PointInt to = edge.getTo();
calculateBound(from);
calculateBound(to);
if (edge instanceof CurvedEdge) {
CurvedEdge curvedEdge = (CurvedEdge) edge;
PointInt control = curvedEdge.getControl();
calculateBound(control);
}
}
private void calculateBound(PointInt point) {
if (point.x < bounds.xMin) {
bounds.xMin = point.x;
}
if (point.x > bounds.xMax) {
bounds.xMax = point.x;
}
if (point.y < bounds.yMin) {
bounds.yMin = point.y;
}
if (point.y > bounds.yMax) {
bounds.yMax = point.y;
}
}
protected void exportFillPath(int groupIndex) {
List<IEdge> path = createPathFromEdgeMap(fillEdgeMaps.get(groupIndex));
List<IEdge> path = createPathFromEdgeMap(_fillEdgeMaps.get(groupIndex));
PointInt pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
int fillStyleIdx = Integer.MAX_VALUE;
if (path.size() > 0) {
beginFills();
for (int i = 0; i < path.size(); i++) {
IEdge e = path.get(i);
calculateBound(e);
if (fillStyleIdx != e.getFillStyleIdx()) {
if (fillStyleIdx != Integer.MAX_VALUE) {
endFill();
@@ -329,7 +296,7 @@ public abstract class ShapeExporterBase implements IShapeExporter {
}
protected void exportLinePath(int groupIndex) {
List<IEdge> path = createPathFromEdgeMap(lineEdgeMaps.get(groupIndex));
List<IEdge> path = createPathFromEdgeMap(_lineEdgeMaps.get(groupIndex));
PointInt pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
int lineStyleIdx = Integer.MAX_VALUE;
LINESTYLE lineStyle;
@@ -337,7 +304,6 @@ public abstract class ShapeExporterBase implements IShapeExporter {
beginLines();
for (int i = 0; i < path.size(); i++) {
IEdge e = path.get(i);
calculateBound(e);
if (lineStyleIdx != e.getLineStyleIdx()) {
lineStyleIdx = e.getLineStyleIdx();
pos = new PointInt(Integer.MAX_VALUE, Integer.MAX_VALUE);
@@ -440,17 +406,17 @@ public abstract class ShapeExporterBase implements IShapeExporter {
int idx;
IEdge prevEdge = null;
List<IEdge> tmpPath = new ArrayList<>();
createCoordMap(subPath);
Map<String, List<IEdge>> coordMap = createCoordMap(subPath);
while (subPath.size() > 0) {
idx = 0;
while (idx < subPath.size()) {
if (prevEdge == null || prevEdge.getTo().equals(subPath.get(idx).getFrom())) {
IEdge edge = subPath.remove(idx);
tmpPath.add(edge);
removeEdgeFromCoordMap(edge);
removeEdgeFromCoordMap(coordMap, edge);
prevEdge = edge;
} else {
IEdge edge = findNextEdgeInCoordMap(prevEdge);
IEdge edge = findNextEdgeInCoordMap(coordMap, prevEdge);
if (edge != null) {
idx = subPath.indexOf(edge);
} else {
@@ -465,8 +431,8 @@ public abstract class ShapeExporterBase implements IShapeExporter {
}
}
protected void createCoordMap(List<IEdge> path) {
coordMap = new HashMap<>();
protected Map<String, List<IEdge>> createCoordMap(List<IEdge> path) {
Map<String, List<IEdge>> coordMap = new HashMap<>();
for (int i = 0; i < path.size(); i++) {
PointInt from = path.get(i).getFrom();
String key = from.x + "_" + from.y;
@@ -479,9 +445,10 @@ public abstract class ShapeExporterBase implements IShapeExporter {
coordMapArray.add(path.get(i));
}
}
return coordMap;
}
protected void removeEdgeFromCoordMap(IEdge edge) {
protected void removeEdgeFromCoordMap(Map<String, List<IEdge>> coordMap, IEdge edge) {
String key = edge.getFrom().x + "_" + edge.getFrom().y;
List<IEdge> coordMapArray = coordMap.get(key);
if (coordMapArray != null) {
@@ -496,7 +463,7 @@ public abstract class ShapeExporterBase implements IShapeExporter {
}
}
protected IEdge findNextEdgeInCoordMap(IEdge edge) {
protected IEdge findNextEdgeInCoordMap(Map<String, List<IEdge>> coordMap, IEdge edge) {
String key = edge.getTo().x + "_" + edge.getTo().y;
List<IEdge> coordMapArray = coordMap.get(key);
if (coordMapArray != null && coordMapArray.size() > 0) {
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.exporters;
/**
*
* @author JPEXS
*/
public class StraightMorphEdge implements IMorphEdge {
protected final PointInt from;
protected final PointInt to;
protected final PointInt fromEnd;
protected final PointInt toEnd;
protected final int lineStyleIdx;
private final int fillStyleIdx;
StraightMorphEdge(PointInt from, PointInt to, PointInt fromEnd, PointInt toEnd, int lineStyleIdx, int fillStyleIdx) {
this.from = from;
this.to = to;
this.fromEnd = fromEnd;
this.toEnd = toEnd;
this.lineStyleIdx = lineStyleIdx;
this.fillStyleIdx = fillStyleIdx;
}
@Override
public PointInt getFrom() {
return from;
}
@Override
public PointInt getTo() {
return to;
}
@Override
public PointInt getFromEnd() {
return fromEnd;
}
@Override
public PointInt getToEnd() {
return toEnd;
}
@Override
public int getLineStyleIdx() {
return lineStyleIdx;
}
@Override
public int getFillStyleIdx() {
return fillStyleIdx;
}
@Override
public IMorphEdge reverseWithNewFillStyle(int newFillStyleIdx) {
return new StraightMorphEdge(to, from, toEnd, fromEnd, lineStyleIdx, newFillStyleIdx);
}
}
@@ -22,8 +22,9 @@ package com.jpexs.decompiler.flash.exporters.modes;
*/
public enum MorphshapeExportMode {
//TODO: implement morphshape export
PNG,
GIF,
AVI
//TODO: implement other morphshape export modes
SVG
//PNG,
//GIF,
//AVI
}
@@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
import com.jpexs.decompiler.flash.exporters.modes.MorphshapeExportMode;
import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
@@ -31,6 +32,7 @@ import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
@@ -68,8 +70,8 @@ public class ExportDialog extends AppDialog {
"scripts",
"binaryData",
"frames",
"fonts"
//,"morphshapes"
"fonts",
"morphshapes"
};
//Display options only when these classes found
@@ -82,8 +84,8 @@ public class ExportDialog extends AppDialog {
{TreeNode.class, ScriptPack.class},
{DefineBinaryDataTag.class},
{FrameNodeItem.class},
{FontTag.class}
//,{MorphShapeTag.class}
{FontTag.class},
{MorphShapeTag.class}
};
//Enum classes for values
@@ -96,8 +98,8 @@ public class ExportDialog extends AppDialog {
ScriptExportMode.class,
BinaryDataExportMode.class,
FramesExportMode.class,
FontExportMode.class
//MorphshapeExportMode.class
FontExportMode.class,
MorphshapeExportMode.class
};
private final JComboBox[] combos;
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
import com.jpexs.decompiler.flash.exporters.modes.MorphshapeExportMode;
import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
@@ -234,7 +235,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
// play morph shape in 2 second(s)
// this settings should be synchronized with frameCount and frameRate
// settings in Mainpanel.createAndShowTempSwf
// settings in MainPanel.createAndShowTempSwf
static final int morphShapeAnimationLength = 2;
static final int morphShapeAnimationFrameRate = 30;
@@ -1138,6 +1139,9 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
if (nodeType == TreeNodeType.SHAPE) {
ret.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.MORPH_SHAPE) {
ret.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.AS) {
ret.add(n);
}
@@ -1183,6 +1187,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
List<ScriptPack> as3scripts = new ArrayList<>();
List<Tag> images = new ArrayList<>();
List<Tag> shapes = new ArrayList<>();
List<Tag> morphshapes = new ArrayList<>();
List<Tag> movies = new ArrayList<>();
List<Tag> sounds = new ArrayList<>();
List<Tag> texts = new ArrayList<>();
@@ -1204,6 +1209,9 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
if (nodeType == TreeNodeType.SHAPE) {
shapes.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.MORPH_SHAPE) {
morphshapes.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.AS) {
as12scripts.add(n);
}
@@ -1258,6 +1266,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
final ScriptExportMode scriptMode = export.getValue(ScriptExportMode.class);
ret.addAll(swf.exportImages(handler, selFile + File.separator + "images", images, export.getValue(ImageExportMode.class)));
ret.addAll(SWF.exportShapes(handler, selFile + File.separator + "shapes", shapes, export.getValue(ShapeExportMode.class)));
ret.addAll(SWF.exportMorphShapes(handler, selFile + File.separator + "morphshapes", morphshapes, export.getValue(MorphshapeExportMode.class)));
ret.addAll(swf.exportTexts(handler, selFile + File.separator + "texts", texts, export.getValue(TextExportMode.class)));
ret.addAll(swf.exportMovies(handler, selFile + File.separator + "movies", movies, export.getValue(MovieExportMode.class)));
ret.addAll(swf.exportSounds(handler, selFile + File.separator + "sounds", sounds, export.getValue(SoundExportMode.class)));
@@ -1666,6 +1675,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec
} else {
swf.exportImages(errorHandler, selFile + File.separator + "images", export.getValue(ImageExportMode.class));
swf.exportShapes(errorHandler, selFile + File.separator + "shapes", export.getValue(ShapeExportMode.class));
swf.exportMorphShapes(errorHandler, selFile + File.separator + "morphshapes", export.getValue(MorphshapeExportMode.class));
swf.exportTexts(errorHandler, selFile + File.separator + "texts", export.getValue(TextExportMode.class));
swf.exportMovies(errorHandler, selFile + File.separator + "movies", export.getValue(MovieExportMode.class));
swf.exportSounds(errorHandler, selFile + File.separator + "sounds", export.getValue(SoundExportMode.class));
@@ -50,6 +50,7 @@ button.cancel = Cancel
morphshapes = Morphshapes
morphshapes.gif = GIF
morphshapes.svg = SVG
frames = Frames
frames.png = PNG
@@ -20,8 +20,10 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.exporters.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.Matrix;
import com.jpexs.decompiler.flash.exporters.Point;
import com.jpexs.decompiler.flash.exporters.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
@@ -96,6 +98,16 @@ public class DefineMorphShape2Tag extends CharacterTag implements MorphShapeTag,
return rect;
}
@Override
public String toSVG() {
ExportRectangle rect = new ExportRectangle(getRect());
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter exporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, rect, new ColorTransform() /*FIXME?*/);
exporter.export();
return exporter.getSVG();
}
@Override
public int getCharacterId() {
return characterId;
@@ -20,8 +20,10 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.exporters.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.Matrix;
import com.jpexs.decompiler.flash.exporters.Point;
import com.jpexs.decompiler.flash.exporters.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
@@ -144,6 +146,16 @@ public class DefineMorphShapeTag extends CharacterTag implements MorphShapeTag,
return rect;
}
@Override
public String toSVG() {
ExportRectangle rect = new ExportRectangle(getRect());
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter exporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, rect, new ColorTransform() /*FIXME?*/);
exporter.export();
return exporter.getSVG();
}
@Override
public RECT getStartBounds() {
return startBounds;
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.exporters.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.Point;
import com.jpexs.decompiler.flash.exporters.SVGShapeExporter;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
@@ -60,7 +61,8 @@ public class DefineShape2Tag extends ShapeTag {
@Override
public String toSVG() {
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), new ColorTransform() /*FIXME?*/);
ExportRectangle rect = new ExportRectangle(getRect());
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), rect, new ColorTransform() /*FIXME?*/);
exporter.export();
return exporter.getSVG();
}
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.exporters.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.Point;
import com.jpexs.decompiler.flash.exporters.SVGShapeExporter;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
@@ -65,7 +66,8 @@ public class DefineShape3Tag extends ShapeTag {
@Override
public String toSVG() {
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), new ColorTransform() /*FIXME?*/);
ExportRectangle rect = new ExportRectangle(getRect());
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), rect, new ColorTransform() /*FIXME?*/);
exporter.export();
return exporter.getSVG();
}
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.exporters.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.Point;
import com.jpexs.decompiler.flash.exporters.SVGShapeExporter;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
@@ -68,7 +69,8 @@ public class DefineShape4Tag extends ShapeTag {
@Override
public String toSVG() {
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), new ColorTransform() /*FIXME?*/);
ExportRectangle rect = new ExportRectangle(getRect());
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), rect, new ColorTransform() /*FIXME?*/);
exporter.export();
return exporter.getSVG();
}
@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.exporters.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.Point;
import com.jpexs.decompiler.flash.exporters.SVGShapeExporter;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
@@ -93,7 +94,8 @@ public class DefineShapeTag extends ShapeTag {
@Override
public String toSVG() {
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), new ColorTransform() /*FIXME?*/);
ExportRectangle rect = new ExportRectangle(getRect());
SVGShapeExporter exporter = new SVGShapeExporter(swf, getShapes(), rect, new ColorTransform() /*FIXME?*/);
exporter.export();
return exporter.getSVG();
}
@@ -40,6 +40,8 @@ public interface MorphShapeTag {
public SHAPE getEndEdges();
public abstract String toSVG();
public int getShapeNum();
public SHAPEWITHSTYLE getShapeAtRatio(int ratio);