Fixed #1679 FLA export - MorphShapes (shape tween)

This commit is contained in:
Jindra Petřík
2022-11-03 19:51:43 +01:00
parent 88440526d6
commit 6d0597eece
3 changed files with 164 additions and 112 deletions
@@ -137,11 +137,13 @@ import com.jpexs.helpers.SerializableImage;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.awt.Font;
import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -156,6 +158,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
@@ -163,11 +167,17 @@ import javax.xml.stream.XMLStreamException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -246,7 +256,7 @@ public class XFLConverter {
}
}
if (!hasMove) {
ret.append("!").append(startX).append(" ").append(startY);
ret.append("! ").append(startX).append(" ").append(startY);
}
for (SHAPERECORD rec : records) {
convertShapeEdge(mat, rec, x, y, ret);
@@ -810,47 +820,34 @@ public class XFLConverter {
strokesNewStr.writeStartElement("strokes");
if (fillStyleCount > 0 || lineStyleCount > 0) {
if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) {
boolean empty = false;
if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) {
if (shapeNum == 4) {
if (strokeStyleOrig > 0) {
if (actualLinestyles != null && !((LINESTYLE2) actualLinestyles.lineStyles2[strokeStyleOrig]).hasFillFlag) {
RGBA color = (RGBA) actualLinestyles.lineStyles2[strokeStyleOrig].getColor();
if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) {
empty = true;
}
}
}
}
if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) {
currentLayer.writeStartElement("Edge");
if (fillStyle0 > -1) {
currentLayer.writeAttribute("fillStyle0", fillStyle0);
}
if (!empty) {
currentLayer.writeStartElement("Edge");
if (fillStyle0 > -1) {
currentLayer.writeAttribute("fillStyle0", fillStyle0);
}
if (fillStyle1 > -1) {
currentLayer.writeAttribute("fillStyle1", fillStyle1);
}
if (strokeStyle > -1) {
currentLayer.writeAttribute("strokeStyle", strokeStyle);
}
StringBuilder edgesSb = new StringBuilder();
convertShapeEdges(startEdgeX, startEdgeY, mat, edges, edgesSb);
currentLayer.writeAttribute("edges", edgesSb.toString());
currentLayer.writeEndElement();
hasEdge = true;
if (fillStyle1 > -1) {
currentLayer.writeAttribute("fillStyle1", fillStyle1);
}
if (strokeStyle > -1) {
currentLayer.writeAttribute("strokeStyle", strokeStyle);
}
StringBuilder edgesSb = new StringBuilder();
convertShapeEdges(startEdgeX, startEdgeY, mat, edges, edgesSb);
currentLayer.writeAttribute("edges", edgesSb.toString());
currentLayer.writeEndElement();
hasEdge = true;
}
}
if (currentLayer.length() > 0) {
currentLayer.writeEndElement(); // edges
currentLayer.writeEndElement(); // DOMShape
}
if (currentLayer.length() > 0 && hasEdge) { //no empty layers
layers.add(currentLayer.toString());
String s = currentLayer.toString();
if (morphshape) {
s = removeOnlyStrokeEdgesBeforeSameFilled(s);
}
layers.add(s);
}
currentLayer.setLength(0);
hasEdge = false;
@@ -905,43 +902,28 @@ public class XFLConverter {
styleChange = true;
}
if (scr.stateLineStyle) {
strokeStyle = scr.lineStyle;// == 0 ? 0 : lineStyleCount - lastLineStyleCount + scr.lineStyle;
strokeStyle = scr.lineStyle;
strokeStyleOrig = scr.lineStyle - 1;
styleChange = true;
}
if (!edges.isEmpty()) {
if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) {
boolean empty = false;
if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) {
if (shapeNum == 4) {
if (strokeStyleOrig > 0) {
if (actualLinestyles != null && !((LINESTYLE2) actualLinestyles.lineStyles2[strokeStyleOrig]).hasFillFlag) {
RGBA color = (RGBA) actualLinestyles.lineStyles2[strokeStyleOrig].getColor();
if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) {
empty = true;
}
}
}
}
if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) {
currentLayer.writeStartElement("Edge");
if (lastFillStyle0 > -1) {
currentLayer.writeAttribute("fillStyle0", lastFillStyle0);
}
if (!empty) {
currentLayer.writeStartElement("Edge");
if (lastFillStyle0 > -1) {
currentLayer.writeAttribute("fillStyle0", lastFillStyle0);
}
if (lastFillStyle1 > -1) {
currentLayer.writeAttribute("fillStyle1", lastFillStyle1);
}
if (lastStrokeStyle > -1) {
currentLayer.writeAttribute("strokeStyle", lastStrokeStyle);
}
StringBuilder edgesSb = new StringBuilder();
convertShapeEdges(startEdgeX, startEdgeY, mat, edges, edgesSb);
currentLayer.writeAttribute("edges", edgesSb.toString());
currentLayer.writeEndElement();
hasEdge = true;
if (lastFillStyle1 > -1) {
currentLayer.writeAttribute("fillStyle1", lastFillStyle1);
}
if (lastStrokeStyle > -1) {
currentLayer.writeAttribute("strokeStyle", lastStrokeStyle);
}
StringBuilder edgesSb = new StringBuilder();
convertShapeEdges(startEdgeX, startEdgeY, mat, edges, edgesSb);
currentLayer.writeAttribute("edges", edgesSb.toString());
currentLayer.writeEndElement();
hasEdge = true;
startEdgeX = x;
startEdgeY = y;
}
@@ -953,38 +935,22 @@ public class XFLConverter {
y = edge.changeY(y);
}
if (!edges.isEmpty()) {
if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) {
boolean empty = false;
if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) {
if (shapeNum == 4) {
if (strokeStyleOrig > 0) {
if (actualLinestyles != null && !((LINESTYLE2) actualLinestyles.lineStyles2[strokeStyleOrig]).hasFillFlag) {
RGBA color = (RGBA) actualLinestyles.lineStyles2[strokeStyleOrig].getColor();
if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) {
empty = true;
}
}
}
}
if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) {
currentLayer.writeStartElement("Edge");
if (fillStyle0 > -1) {
currentLayer.writeAttribute("fillStyle0", fillStyle0);
}
if (!empty) {
currentLayer.writeStartElement("Edge");
if (fillStyle0 > -1) {
currentLayer.writeAttribute("fillStyle0", fillStyle0);
}
if (fillStyle1 > -1) {
currentLayer.writeAttribute("fillStyle1", fillStyle1);
}
if (strokeStyle > -1) {
currentLayer.writeAttribute("strokeStyle", strokeStyle);
}
StringBuilder edgesSb = new StringBuilder();
convertShapeEdges(startEdgeX, startEdgeY, mat, edges, edgesSb);
currentLayer.writeAttribute("edges", edgesSb.toString());
currentLayer.writeEndElement();
hasEdge = true;
if (fillStyle1 > -1) {
currentLayer.writeAttribute("fillStyle1", fillStyle1);
}
if (strokeStyle > -1) {
currentLayer.writeAttribute("strokeStyle", strokeStyle);
}
StringBuilder edgesSb = new StringBuilder();
convertShapeEdges(startEdgeX, startEdgeY, mat, edges, edgesSb);
currentLayer.writeAttribute("edges", edgesSb.toString());
currentLayer.writeEndElement();
hasEdge = true;
}
}
edges.clear();
@@ -993,11 +959,86 @@ public class XFLConverter {
currentLayer.writeEndElement(); // DOMShape
if (currentLayer.length() > 0 && hasEdge) { //no empty layers
layers.add(currentLayer.toString());
String s = currentLayer.toString();
if (morphshape) {
s = removeOnlyStrokeEdgesBeforeSameFilled(s);
}
layers.add(s);
}
}
return layers;
}
/**
* A hack. This will remove a stroked path with no fill which has same stroke as subsequent path.
* This happens in the morphshape edges. This needs to be cleaned up before exporting to FLA.
*
* @param layer
* @return
*/
private static String removeOnlyStrokeEdgesBeforeSameFilled(String layer) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(false);
dbf.setValidating(false);
DocumentBuilder db;
try {
db = dbf.newDocumentBuilder();
String docString = "<x>" + layer + "</x>";
Document doc = db.parse(new ByteArrayInputStream(docString.getBytes("UTF-8")));
NodeList edgesParentList = doc.getElementsByTagName("edges");
String prevStrokeOnly = null;
String prevEdgesStr = "";
for (int j = 0; j < edgesParentList.getLength(); j++) {
Node edgesParent = edgesParentList.item(j);
NodeList edges = edgesParent.getChildNodes();
Node prevNode = null;
for (int i = 0; i < edges.getLength(); i++) {
Node edge = edges.item(i);
if (edge.getNodeType() == Node.TEXT_NODE) {
continue;
}
NamedNodeMap attributes = edge.getAttributes();
Node strokeStyleNode = attributes.getNamedItem("strokeStyle");
Node fillStyle0Node = attributes.getNamedItem("fillStyle0");
Node fillStyle1Node = attributes.getNamedItem("fillStyle1");
Node edgesNode = attributes.getNamedItem("edges");
String edgesStr = edgesNode.getNodeValue();
String strokeStyle = strokeStyleNode != null ? strokeStyleNode.getNodeValue() : null;
String fillStyle0 = fillStyle0Node != null ? fillStyle0Node.getNodeValue() : null;
String fillStyle1 = fillStyle1Node != null ? fillStyle1Node.getNodeValue() : null;
if (prevStrokeOnly != null &&
strokeStyle != null &&
strokeStyle.equals(prevStrokeOnly) &&
prevEdgesStr.equals(edgesStr)) {
Node edgeToRemove = prevNode;
edgeToRemove.getParentNode().removeChild(edgeToRemove);
}
prevStrokeOnly = null;
if (strokeStyle != null && fillStyle0 == null && fillStyle1 == null) {
prevStrokeOnly = strokeStyle;
}
prevNode = edge;
prevEdgesStr = edgesStr;
}
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(writer));
String output = writer.getBuffer().toString();
output = output.trim();
output = output.substring(3, output.length() - 4);
return output;
} catch (TransformerException | SAXException | IOException | ParserConfigurationException ex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex);
}
return layer;
}
private static int getLayerCount(ReadOnlyTagList tags) {
int maxDepth = 0;
@@ -2269,8 +2310,8 @@ public class XFLConverter {
int characterId = -1;
int ratio = -1;
boolean shapeTween = false;
boolean lastShapeTween = false;
MorphShapeTag shapeTweener = null;
int lastTweenRatio = -1;
//Add ShowFrameTag to the end when there is one last missing
List<Tag> timTags = timelineTags.toArrayList();
@@ -2360,7 +2401,8 @@ public class XFLConverter {
if (shapeTween && character != null) {
MorphShapeTag m = (MorphShapeTag) character;
shapeTweener = m;
shapeTween = false;
shapeTween = false;
lastTweenRatio = ratio;
}
character = null;
metadata = null;
@@ -2384,7 +2426,18 @@ public class XFLConverter {
if (frame + 1 <= endFrame) {
lastIn = true;
if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId) || shapeTweener != null)) {
if (shapeTweener != null) {
MorphShapeTag m = shapeTweener;
XFLXmlWriter addLastWriter = new XFLXmlWriter();
SHAPEWITHSTYLE endShape = m.getShapeAtRatio(lastTweenRatio);
convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter);
duration--;
convertFrame(true, null, null, frame - duration, duration, "", lastElements, files, writer2);
duration = 1;
lastElements = addLastWriter.toString();
shapeTweener = null;
}
if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) { // || shapeTweener != null)) {
ShapeTag shape = (ShapeTag) character;
convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter);
shapeTween = false;
@@ -2409,7 +2462,7 @@ public class XFLConverter {
frame++;
String elements = elementsWriter.toString();
if (!elements.equals(lastElements) && frame > 0) {
convertFrame(lastShapeTween, null, null, frame - duration, duration, "", lastElements, files, writer2);
convertFrame(false, null, null, frame - duration, duration, "", lastElements, files, writer2);
duration = 1;
} else if (frame == 0) {
duration = 1;
@@ -2417,19 +2470,16 @@ public class XFLConverter {
duration++;
}
lastShapeTween = shapeTween;
lastElements = elements;
if (frame > endFrame) {
if (lastIn) {
lastElements = "";
lastShapeTween = false;
lastIn = false;
}
}
} else {
if (lastIn) {
lastElements = "";
lastShapeTween = false;
lastIn = false;
}
frame++;
@@ -2443,7 +2493,7 @@ public class XFLConverter {
}
if (!lastElements.isEmpty()) {
frame++;
convertFrame(lastShapeTween, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files, writer2);
convertFrame(false, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files, writer2);
}
afterStr = "</frames>" + afterStr;