Saving files before refreshing line endings

This commit is contained in:
Jindra Petřík
2018-01-07 17:18:39 +01:00
parent cfb3931fcc
commit 0cb15e9c76
14 changed files with 7667 additions and 7667 deletions

View File

@@ -1,182 +1,182 @@
/*
* 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.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.types.ConvertData;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.cache.ScriptDecompiledListener;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.HighlightedText;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.helpers.ImmediateFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class DecompilerPool {
private final ThreadPoolExecutor executor;
public DecompilerPool() {
int threadCount = Configuration.getParallelThreadCount();
executor = new ThreadPoolExecutor(threadCount, threadCount,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
}
public Future<HighlightedText> submitTask(ASMSource src, ActionList actions, ScriptDecompiledListener<HighlightedText> listener) {
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
public HighlightedText call() throws Exception {
if (listener != null) {
listener.onStart();
}
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
writer.startFunction("!script");
src.getActionScriptSource(writer, actions);
writer.endFunction();
HighlightedText result = new HighlightedText(writer);
SWF swf = src.getSwf();
if (swf != null) {
swf.as2Cache.put(src, result);
}
if (listener != null) {
listener.onComplete(result);
}
return result;
}
};
return submit(callable);
}
public Future<HighlightedText> submitTask(ScriptPack pack, ScriptDecompiledListener<HighlightedText> listener) {
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
public HighlightedText call() throws Exception {
if (listener != null) {
listener.onStart();
}
int scriptIndex = pack.scriptIndex;
ScriptInfo script = null;
if (scriptIndex > -1) {
script = pack.abc.script_info.get(scriptIndex);
}
boolean parallel = Configuration.parallelSpeedUp.get();
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
pack.toSource(writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel);
HighlightedText result = new HighlightedText(writer);
SWF swf = pack.getSwf();
if (swf != null) {
swf.as3Cache.put(pack, result);
}
if (listener != null) {
listener.onComplete(result);
}
return result;
}
};
return submit(callable);
}
private Future<HighlightedText> submit(Callable<HighlightedText> callable) {
boolean parallel = Configuration.parallelSpeedUp.get();
if (parallel) {
Future<HighlightedText> f = executor.submit(callable);
return f;
} else {
boolean cancelled = false;
Throwable throwable = null;
HighlightedText result = null;
try {
result = callable.call();
} catch (InterruptedException ex) {
cancelled = true;
} catch (Exception ex) {
throwable = ex;
}
return new ImmediateFuture<>(result, throwable, cancelled);
}
}
public String getStat() {
return "core: " + executor.getCorePoolSize()
+ " size: " + executor.getPoolSize()
+ " largest: " + executor.getLargestPoolSize()
+ " max: " + executor.getMaximumPoolSize()
+ " active: " + executor.getActiveCount()
+ " count: " + executor.getTaskCount()
+ " completed: " + executor.getCompletedTaskCount();
}
public HighlightedText decompile(ASMSource src, ActionList actions) throws InterruptedException {
Future<HighlightedText> future = submitTask(src, actions, null);
try {
return future.get();
} catch (InterruptedException ex) {
future.cancel(true);
throw ex;
} catch (ExecutionException ex) {
Logger.getLogger(DecompilerPool.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public HighlightedText decompile(ScriptPack pack) throws InterruptedException {
Future<HighlightedText> future = submitTask(pack, null);
try {
return future.get();
} catch (InterruptedException ex) {
future.cancel(true);
throw ex;
} catch (ExecutionException ex) {
Logger.getLogger(DecompilerPool.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public void shutdown() throws InterruptedException {
executor.shutdown();
if (!executor.awaitTermination(100, TimeUnit.SECONDS)) {
}
}
}
/*
* 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.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.types.ConvertData;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.action.ActionList;
import com.jpexs.decompiler.flash.cache.ScriptDecompiledListener;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.HighlightedText;
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.helpers.ImmediateFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class DecompilerPool {
private final ThreadPoolExecutor executor;
public DecompilerPool() {
int threadCount = Configuration.getParallelThreadCount();
executor = new ThreadPoolExecutor(threadCount, threadCount,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
}
public Future<HighlightedText> submitTask(ASMSource src, ActionList actions, ScriptDecompiledListener<HighlightedText> listener) {
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
public HighlightedText call() throws Exception {
if (listener != null) {
listener.onStart();
}
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
writer.startFunction("!script");
src.getActionScriptSource(writer, actions);
writer.endFunction();
HighlightedText result = new HighlightedText(writer);
SWF swf = src.getSwf();
if (swf != null) {
swf.as2Cache.put(src, result);
}
if (listener != null) {
listener.onComplete(result);
}
return result;
}
};
return submit(callable);
}
public Future<HighlightedText> submitTask(ScriptPack pack, ScriptDecompiledListener<HighlightedText> listener) {
Callable<HighlightedText> callable = new Callable<HighlightedText>() {
@Override
public HighlightedText call() throws Exception {
if (listener != null) {
listener.onStart();
}
int scriptIndex = pack.scriptIndex;
ScriptInfo script = null;
if (scriptIndex > -1) {
script = pack.abc.script_info.get(scriptIndex);
}
boolean parallel = Configuration.parallelSpeedUp.get();
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
pack.toSource(writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel);
HighlightedText result = new HighlightedText(writer);
SWF swf = pack.getSwf();
if (swf != null) {
swf.as3Cache.put(pack, result);
}
if (listener != null) {
listener.onComplete(result);
}
return result;
}
};
return submit(callable);
}
private Future<HighlightedText> submit(Callable<HighlightedText> callable) {
boolean parallel = Configuration.parallelSpeedUp.get();
if (parallel) {
Future<HighlightedText> f = executor.submit(callable);
return f;
} else {
boolean cancelled = false;
Throwable throwable = null;
HighlightedText result = null;
try {
result = callable.call();
} catch (InterruptedException ex) {
cancelled = true;
} catch (Exception ex) {
throwable = ex;
}
return new ImmediateFuture<>(result, throwable, cancelled);
}
}
public String getStat() {
return "core: " + executor.getCorePoolSize()
+ " size: " + executor.getPoolSize()
+ " largest: " + executor.getLargestPoolSize()
+ " max: " + executor.getMaximumPoolSize()
+ " active: " + executor.getActiveCount()
+ " count: " + executor.getTaskCount()
+ " completed: " + executor.getCompletedTaskCount();
}
public HighlightedText decompile(ASMSource src, ActionList actions) throws InterruptedException {
Future<HighlightedText> future = submitTask(src, actions, null);
try {
return future.get();
} catch (InterruptedException ex) {
future.cancel(true);
throw ex;
} catch (ExecutionException ex) {
Logger.getLogger(DecompilerPool.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public HighlightedText decompile(ScriptPack pack) throws InterruptedException {
Future<HighlightedText> future = submitTask(pack, null);
try {
return future.get();
} catch (InterruptedException ex) {
future.cancel(true);
throw ex;
} catch (ExecutionException ex) {
Logger.getLogger(DecompilerPool.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public void shutdown() throws InterruptedException {
executor.shutdown();
if (!executor.awaitTermination(100, TimeUnit.SECONDS)) {
}
}
}

View File

@@ -1,269 +1,269 @@
/*
* 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.exporters.commonshape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.helpers.Helper;
import java.awt.Color;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
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.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
*
* @author JPEXS
*/
public class SVGExporter {
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 CDATASection _svgStyle;
protected Stack<Element> _svgGs = new Stack<>();
public List<Element> gradients;
protected int lastPatternId;
public Map<Tag, String> exportedTags = new HashMap<>();
public Map<Tag, Map<Integer, String>> exportedChars = new HashMap<>();
private final Map<String, Integer> lastIds = new HashMap<>();
private final HashSet<String> fontFaces = new HashSet<>();
public boolean useTextTag = Configuration.textExportExportFontFace.get();
public SVGExporter(ExportRectangle bounds, double zoom) {
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);
if (bounds != null) {
svgRoot.setAttribute("width", (bounds.getWidth() / SWF.unitDivisor) + "px");
svgRoot.setAttribute("height", (bounds.getHeight() / SWF.unitDivisor) + "px");
createDefGroup(bounds, null, zoom);
}
} catch (ParserConfigurationException ex) {
Logger.getLogger(SVGExporter.class.getName()).log(Level.SEVERE, null, ex);
}
gradients = new ArrayList<>();
}
private Element getDefs() {
if (_svgDefs == null) {
_svgDefs = _svg.createElement("defs");
_svg.getDocumentElement().appendChild(_svgDefs);
}
return _svgDefs;
}
private CDATASection getStyle() {
if (_svgStyle == null) {
Element style = _svg.createElement("style");
_svgStyle = _svg.createCDATASection("");
style.appendChild(_svgStyle);
getDefs().appendChild(style);
}
return _svgStyle;
}
public final void createDefGroup(ExportRectangle bounds, String id) {
createDefGroup(bounds, id, 1);
}
public final void createDefGroup(ExportRectangle bounds, String id, double zoom) {
Element g = _svg.createElement("g");
if (bounds != null) {
Matrix mat = Matrix.getTranslateInstance(-bounds.xMin, -bounds.yMin);
mat.scale(zoom);
g.setAttribute("transform", mat.getSvgTransformationString(SWF.unitDivisor, 1));
}
if (id != null) {
g.setAttribute("id", id);
}
if (_svgGs.size() == 0) {
_svg.getDocumentElement().appendChild(g);
} else {
getDefs().appendChild(g);
}
_svgGs.add(g);
}
public boolean endGroup() {
Element g = _svgGs.pop();
if (g.getChildNodes().getLength() == 0) {
g.getParentNode().removeChild(g);
return false;
}
return true;
}
public final Element createSubGroup(Matrix transform, String id) {
Element group = createSubGroup(id, "g");
if (transform != null) {
group.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1));
}
return group;
}
public final Element createClipPath(Matrix transform, String id) {
Element group = createSubGroup(id, "clipPath");
Node parent = group.getParentNode();
if (parent instanceof Element) {
Element parentElement = (Element) parent;
group.setAttribute("transform", parentElement.getAttribute("transform"));
}
return group;
}
private Element createSubGroup(String id, String tagName) {
Element group = _svg.createElement(tagName);
if (id != null) {
group.setAttribute("id", id);
}
_svgGs.peek().appendChild(group);
_svgGs.add(group);
return group;
}
public void addToGroup(Node newChild) {
_svgGs.peek().appendChild(newChild);
}
public void addToDefs(Node newChild) {
getDefs().appendChild(newChild);
}
public Element createElement(String tagName) {
return _svg.createElement(tagName);
}
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(SVGExporter.class.getName()).log(Level.SEVERE, null, ex);
}
return writer.toString();
}
public void setBackGroundColor(Color backGroundColor) {
Attr attr = _svg.createAttribute("style");
attr.setValue("background: " + new RGBA(backGroundColor).toHexARGB());
}
public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName) {
Element image = _svg.createElement("use");
if (transform != null) {
image.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1));
image.setAttribute("width", Double.toString(boundRect.getWidth() / (double) SWF.unitDivisor));
image.setAttribute("height", Double.toString(boundRect.getHeight() / (double) SWF.unitDivisor));
}
if (instanceName != null) {
image.setAttribute("id", instanceName);
}
image.setAttribute("xlink:href", "#" + href);
_svgGs.peek().appendChild(image);
return image;
}
public void addStyle(String fontFace, byte[] data, FontExportMode mode) {
if (!fontFaces.contains(fontFace)) {
fontFaces.add(fontFace);
String base64Data = Helper.byteArrayToBase64String(data);
String value = getStyle().getTextContent();
value += Helper.newLine;
value += " @font-face {" + Helper.newLine;
value += " font-family: \"" + fontFace + "\";" + Helper.newLine;
switch (mode) {
case TTF:
value += " src: url('data:font/truetype;base64," + base64Data + "') format(\"truetype\");" + Helper.newLine;
break;
case WOFF:
value += " src: url('data:font/woff;base64," + base64Data + "') format(\"woff\");" + Helper.newLine;
break;
}
value += " }" + Helper.newLine;
getStyle().setTextContent(value);
}
}
public String getUniqueId(String prefix) {
Integer lastId = lastIds.get(prefix);
if (lastId == null) {
lastId = 0;
} else {
lastId++;
}
lastIds.put(prefix, lastId);
return prefix + lastId;
}
protected static double roundPixels20(double pixels) {
return Math.round(pixels * 100) / 100.0;
}
}
/*
* 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.exporters.commonshape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.helpers.Helper;
import java.awt.Color;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
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.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
*
* @author JPEXS
*/
public class SVGExporter {
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 CDATASection _svgStyle;
protected Stack<Element> _svgGs = new Stack<>();
public List<Element> gradients;
protected int lastPatternId;
public Map<Tag, String> exportedTags = new HashMap<>();
public Map<Tag, Map<Integer, String>> exportedChars = new HashMap<>();
private final Map<String, Integer> lastIds = new HashMap<>();
private final HashSet<String> fontFaces = new HashSet<>();
public boolean useTextTag = Configuration.textExportExportFontFace.get();
public SVGExporter(ExportRectangle bounds, double zoom) {
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);
if (bounds != null) {
svgRoot.setAttribute("width", (bounds.getWidth() / SWF.unitDivisor) + "px");
svgRoot.setAttribute("height", (bounds.getHeight() / SWF.unitDivisor) + "px");
createDefGroup(bounds, null, zoom);
}
} catch (ParserConfigurationException ex) {
Logger.getLogger(SVGExporter.class.getName()).log(Level.SEVERE, null, ex);
}
gradients = new ArrayList<>();
}
private Element getDefs() {
if (_svgDefs == null) {
_svgDefs = _svg.createElement("defs");
_svg.getDocumentElement().appendChild(_svgDefs);
}
return _svgDefs;
}
private CDATASection getStyle() {
if (_svgStyle == null) {
Element style = _svg.createElement("style");
_svgStyle = _svg.createCDATASection("");
style.appendChild(_svgStyle);
getDefs().appendChild(style);
}
return _svgStyle;
}
public final void createDefGroup(ExportRectangle bounds, String id) {
createDefGroup(bounds, id, 1);
}
public final void createDefGroup(ExportRectangle bounds, String id, double zoom) {
Element g = _svg.createElement("g");
if (bounds != null) {
Matrix mat = Matrix.getTranslateInstance(-bounds.xMin, -bounds.yMin);
mat.scale(zoom);
g.setAttribute("transform", mat.getSvgTransformationString(SWF.unitDivisor, 1));
}
if (id != null) {
g.setAttribute("id", id);
}
if (_svgGs.size() == 0) {
_svg.getDocumentElement().appendChild(g);
} else {
getDefs().appendChild(g);
}
_svgGs.add(g);
}
public boolean endGroup() {
Element g = _svgGs.pop();
if (g.getChildNodes().getLength() == 0) {
g.getParentNode().removeChild(g);
return false;
}
return true;
}
public final Element createSubGroup(Matrix transform, String id) {
Element group = createSubGroup(id, "g");
if (transform != null) {
group.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1));
}
return group;
}
public final Element createClipPath(Matrix transform, String id) {
Element group = createSubGroup(id, "clipPath");
Node parent = group.getParentNode();
if (parent instanceof Element) {
Element parentElement = (Element) parent;
group.setAttribute("transform", parentElement.getAttribute("transform"));
}
return group;
}
private Element createSubGroup(String id, String tagName) {
Element group = _svg.createElement(tagName);
if (id != null) {
group.setAttribute("id", id);
}
_svgGs.peek().appendChild(group);
_svgGs.add(group);
return group;
}
public void addToGroup(Node newChild) {
_svgGs.peek().appendChild(newChild);
}
public void addToDefs(Node newChild) {
getDefs().appendChild(newChild);
}
public Element createElement(String tagName) {
return _svg.createElement(tagName);
}
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(SVGExporter.class.getName()).log(Level.SEVERE, null, ex);
}
return writer.toString();
}
public void setBackGroundColor(Color backGroundColor) {
Attr attr = _svg.createAttribute("style");
attr.setValue("background: " + new RGBA(backGroundColor).toHexARGB());
}
public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName) {
Element image = _svg.createElement("use");
if (transform != null) {
image.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1));
image.setAttribute("width", Double.toString(boundRect.getWidth() / (double) SWF.unitDivisor));
image.setAttribute("height", Double.toString(boundRect.getHeight() / (double) SWF.unitDivisor));
}
if (instanceName != null) {
image.setAttribute("id", instanceName);
}
image.setAttribute("xlink:href", "#" + href);
_svgGs.peek().appendChild(image);
return image;
}
public void addStyle(String fontFace, byte[] data, FontExportMode mode) {
if (!fontFaces.contains(fontFace)) {
fontFaces.add(fontFace);
String base64Data = Helper.byteArrayToBase64String(data);
String value = getStyle().getTextContent();
value += Helper.newLine;
value += " @font-face {" + Helper.newLine;
value += " font-family: \"" + fontFace + "\";" + Helper.newLine;
switch (mode) {
case TTF:
value += " src: url('data:font/truetype;base64," + base64Data + "') format(\"truetype\");" + Helper.newLine;
break;
case WOFF:
value += " src: url('data:font/woff;base64," + base64Data + "') format(\"woff\");" + Helper.newLine;
break;
}
value += " }" + Helper.newLine;
getStyle().setTextContent(value);
}
}
public String getUniqueId(String prefix) {
Integer lastId = lastIds.get(prefix);
if (lastId == null) {
lastId = 0;
} else {
lastId++;
}
lastIds.put(prefix, lastId);
return prefix + lastId;
}
protected static double roundPixels20(double pixels) {
return Math.round(pixels * 100) / 100.0;
}
}

View File

@@ -1,463 +1,463 @@
/*
* 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.exporters.morphshape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
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 org.w3c.dom.Element;
/**
*
* @author JPEXS, Claus Wahlers
*/
public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter {
protected Element path;
protected int id;
protected int lastPatternId;
private final Color defaultColor;
private final SWF swf;
private final SVGExporter exporter;
public SVGMorphShapeExporter(SWF swf, SHAPE shape, SHAPE endShape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) {
super(shape, endShape, colorTransform, zoom);
this.swf = swf;
this.id = id;
this.defaultColor = defaultColor;
this.exporter = exporter;
}
@Override
public void beginFill(RGB color, RGB colorEnd) {
if (color == null) {
color = new RGB(defaultColor == null ? Color.black : defaultColor);
}
if (colorEnd == null) {
colorEnd = new RGB(defaultColor == null ? Color.black : defaultColor);
}
finalizePath();
path.setAttribute("stroke", "none");
path.setAttribute("fill", color.toHexRGB());
path.setAttribute("fill-rule", "evenodd");
path.appendChild(createAnimateElement("fill", color.toHexRGB(), colorEnd.toHexRGB()));
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
path.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat()));
}
RGBA colorAEnd = (RGBA) colorEnd;
path.appendChild(createAnimateElement("fill-opacity", colorA.getAlphaFloat(), colorAEnd.getAlphaFloat()));
}
}
@Override
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, GRADRECORD[] gradientRecordsEnd, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio, float focalPointRatioEnd) {
finalizePath();
Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, gradientRecordsEnd, matrix, matrixEnd, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "none");
path.setAttribute("fill", "url(#gradient" + id + ")");
path.setAttribute("fill-rule", "evenodd");
exporter.addToDefs(gradient);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, Matrix matrixEnd, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImageCached();
if (img != null) {
if (colorTransform != null) {
colorTransform.apply(img);
}
int width = img.getWidth();
int height = img.getHeight();
lastPatternId++;
String patternId = "PatternID_" + id + "_" + lastPatternId;
ImageFormat format = image.getImageFormat();
byte[] imageData = Helper.readStream(image.getImageData());
String base64ImgData = Helper.byteArrayToBase64String(imageData);
path.setAttribute("style", "fill:url(#" + patternId + ")");
Element pattern = exporter.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) {
matrix = matrix.clone();
matrix.rotateSkew0 *= zoom / SWF.unitDivisor;
matrix.rotateSkew1 *= zoom / SWF.unitDivisor;
matrix.scaleX *= zoom / SWF.unitDivisor;
matrix.scaleY *= zoom / SWF.unitDivisor;
matrixEnd = matrixEnd.clone();
matrixEnd.rotateSkew0 *= zoom / SWF.unitDivisor;
matrixEnd.rotateSkew1 *= zoom / SWF.unitDivisor;
matrixEnd.scaleX *= zoom / SWF.unitDivisor;
matrixEnd.scaleY *= zoom / SWF.unitDivisor;
addMatrixAnimation(pattern, "patternTransform", matrix, matrixEnd);
}
Element imageElement = exporter.createElement("image");
imageElement.setAttribute("width", "" + width);
imageElement.setAttribute("height", "" + height);
imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData);
pattern.appendChild(imageElement);
exporter.addToGroup(pattern);
}
}
}
@Override
public void lineStyle(double thickness, double thicknessEnd, RGB color, RGB colorEnd, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
finalizePath();
thickness *= zoom / SWF.unitDivisor;
thicknessEnd *= zoom / SWF.unitDivisor;
path.setAttribute("fill", "none");
if (color != null) {
path.setAttribute("stroke", color.toHexRGB());
path.appendChild(createAnimateElement("stroke", color.toHexRGB(), colorEnd.toHexRGB()));
}
path.setAttribute("stroke-width", Double.toString(thickness == 0 ? 1 : thickness));
path.appendChild(createAnimateElement("stroke-width", thickness, thicknessEnd));
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
path.setAttribute("stroke-opacity", Float.toString(colorA.getAlphaFloat()));
}
RGBA colorAEnd = (RGBA) colorEnd;
path.appendChild(createAnimateElement("fill-opacity", colorA.getAlphaFloat(), colorAEnd.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 != 4f) {
path.setAttribute("stroke-miterlimit", Double.toString(miterLimit));
}
break;
}
}
@Override
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, GRADRECORD[] gradientRecordsEnd, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio, float focalPointRatioEnd) {
path.removeAttribute("stroke-opacity");
Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, gradientRecordsEnd, matrix, matrixEnd, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "url(#gradient" + id + ")");
path.setAttribute("fill", "none");
exporter.addToDefs(gradient);
}
private Element createAnimateElement(String attributeName, Object startValue, Object endValue) {
Element animate = exporter.createElement("animate");
animate.setAttribute("dur", "2s"); // todo
animate.setAttribute("repeatCount", "indefinite");
animate.setAttribute("attributeName", attributeName);
animate.setAttribute("values", startValue + ";" + endValue);
return animate;
}
@Override
protected void finalizePath() {
if (path != null && pathData != null && pathData.length() > 0) {
path.setAttribute("d", pathData.toString().trim());
path.appendChild(createAnimateElement("d", pathData.toString().trim(), pathDataEnd.toString().trim()));
exporter.addToGroup(path);
}
path = exporter.createElement("path");
super.finalizePath();
}
private void addMatrixAnimation(Element element, String attribute, Matrix matrix, Matrix matrixEnd) {
final int animationLength = 2; // todo
final String animationLengthStr = animationLength + "s";
element.setAttribute(attribute, matrix.getSvgTransformationString(SWF.unitDivisor / zoom, 1));
// QR decomposition
double translateX = roundPixels400(matrix.translateX * zoom / SWF.unitDivisor);
double translateY = roundPixels400(matrix.translateY * zoom / SWF.unitDivisor);
double a = matrix.scaleX;
double b = matrix.rotateSkew0;
double c = matrix.rotateSkew1;
double d = matrix.scaleY;
double r = Math.sqrt(a * a + b * b);
double det = a * d - b * c;
double rotate = roundPixels400(Math.signum(b) * Math.acos(a / r) * 180 / Math.PI);
double scaleX = roundPixels400(r);
double scaleY = roundPixels400(det / r);
double skewX = roundPixels400(Math.atan((a * c + b * d) / (r * r)) * 180 / Math.PI);
double translateXEnd = roundPixels400(matrixEnd.translateX * zoom / SWF.unitDivisor);
double translateYEnd = roundPixels400(matrixEnd.translateY * zoom / SWF.unitDivisor);
a = matrixEnd.scaleX;
b = matrixEnd.rotateSkew0;
c = matrixEnd.rotateSkew1;
d = matrixEnd.scaleY;
double rEnd = Math.sqrt(a * a + b * b);
double detEnd = a * d - b * c;
double rotateEnd = roundPixels400(Math.signum(b) * Math.acos(a / rEnd) * 180 / Math.PI);
double scaleXEnd = roundPixels400(rEnd);
double scaleYEnd = roundPixels400(detEnd / rEnd);
double skewXEnd = roundPixels400(Math.atan((a * c + b * d) / (rEnd * rEnd)) * 180 / Math.PI);
Element animateClear = exporter.createElement("animateTransform");
animateClear.setAttribute("dur", animationLengthStr);
animateClear.setAttribute("repeatCount", "indefinite");
animateClear.setAttribute("attributeName", attribute);
animateClear.setAttribute("type", "scale");
animateClear.setAttribute("additive", "replace");
animateClear.setAttribute("from", "1");
animateClear.setAttribute("to", "1");
Element animateTranslate = exporter.createElement("animateTransform");
animateTranslate.setAttribute("dur", animationLengthStr);
animateTranslate.setAttribute("repeatCount", "indefinite");
animateTranslate.setAttribute("attributeName", attribute);
animateTranslate.setAttribute("type", "translate");
animateTranslate.setAttribute("additive", "sum");
animateTranslate.setAttribute("from", translateX + " " + translateY);
animateTranslate.setAttribute("to", translateXEnd + " " + translateYEnd);
Element animateRotate = exporter.createElement("animateTransform");
animateRotate.setAttribute("dur", animationLengthStr);
animateRotate.setAttribute("repeatCount", "indefinite");
animateRotate.setAttribute("attributeName", attribute);
animateRotate.setAttribute("type", "rotate");
animateRotate.setAttribute("additive", "sum");
animateRotate.setAttribute("from", Double.toString(rotate));
animateRotate.setAttribute("to", Double.toString(rotateEnd));
Element animateScale = exporter.createElement("animateTransform");
animateScale.setAttribute("dur", animationLengthStr);
animateScale.setAttribute("repeatCount", "indefinite");
animateScale.setAttribute("attributeName", attribute);
animateScale.setAttribute("type", "scale");
animateScale.setAttribute("additive", "sum");
animateScale.setAttribute("from", scaleX + " " + scaleY);
animateScale.setAttribute("to", scaleXEnd + " " + scaleYEnd);
Element animateSkewX = exporter.createElement("animateTransform");
animateSkewX.setAttribute("dur", animationLengthStr);
animateSkewX.setAttribute("repeatCount", "indefinite");
animateSkewX.setAttribute("attributeName", attribute);
animateSkewX.setAttribute("type", "skewX");
animateSkewX.setAttribute("additive", "sum");
animateSkewX.setAttribute("from", Double.toString(skewX));
animateSkewX.setAttribute("to", Double.toString(skewXEnd));
element.appendChild(animateClear);
element.appendChild(animateTranslate);
element.appendChild(animateRotate);
element.appendChild(animateScale);
element.appendChild(animateSkewX);
/*
// LDU decomposition
double translateX = roundPixels400(matrix.translateX * zoom / SWF.unitDivisor);
double translateY = roundPixels400(matrix.translateY * zoom / SWF.unitDivisor);
double a = matrix.scaleX;
double b = matrix.rotateSkew0;
double c = matrix.rotateSkew1;
double d = matrix.scaleY;
double det = a * d - b * c;
double skewY = roundPixels400(Math.atan2(b, a) * 180 / Math.PI);
double scaleX = roundPixels400(a);
double scaleY = roundPixels400(det / a);
double skewX = roundPixels400(Math.atan2(c, a) * 180 / Math.PI);
double translateXEnd = roundPixels400(matrixEnd.translateX * zoom / SWF.unitDivisor);
double translateYEnd = roundPixels400(matrixEnd.translateY * zoom / SWF.unitDivisor);
a = matrixEnd.scaleX;
b = matrixEnd.rotateSkew0;
c = matrixEnd.rotateSkew1;
d = matrixEnd.scaleY;
double detEnd = a * d - b * c;
double skewYEnd = roundPixels400(Math.atan2(b, a) * 180 / Math.PI);
double scaleXEnd = roundPixels400(a);
double scaleYEnd = roundPixels400(detEnd / a);
double skewXEnd = roundPixels400(Math.atan2(c, a) * 180 / Math.PI);
Element animateClear = exporter.createElement("animateTransform");
animateClear.setAttribute("dur", animationLengthStr);
animateClear.setAttribute("repeatCount", "indefinite");
animateClear.setAttribute("attributeName", attribute);
animateClear.setAttribute("type", "scale");
animateClear.setAttribute("additive", "replace");
animateClear.setAttribute("from", "1");
animateClear.setAttribute("to", "1");
Element animateTranslate = exporter.createElement("animateTransform");
animateTranslate.setAttribute("dur", animationLengthStr);
animateTranslate.setAttribute("repeatCount", "indefinite");
animateTranslate.setAttribute("attributeName", attribute);
animateTranslate.setAttribute("type", "translate");
animateTranslate.setAttribute("additive", "sum");
animateTranslate.setAttribute("from", translateX + " " + translateY);
animateTranslate.setAttribute("to", translateXEnd + " " + translateYEnd);
Element animateSkewY = exporter.createElement("animateTransform");
animateSkewY.setAttribute("dur", animationLengthStr);
animateSkewY.setAttribute("repeatCount", "indefinite");
animateSkewY.setAttribute("attributeName", attribute);
animateSkewY.setAttribute("type", "skewY");
animateSkewY.setAttribute("additive", "sum");
animateSkewY.setAttribute("from", Double.toString(skewY));
animateSkewY.setAttribute("to", Double.toString(skewYEnd));
Element animateScale = exporter.createElement("animateTransform");
animateScale.setAttribute("dur", animationLengthStr);
animateScale.setAttribute("repeatCount", "indefinite");
animateScale.setAttribute("attributeName", attribute);
animateScale.setAttribute("type", "scale");
animateScale.setAttribute("additive", "sum");
animateScale.setAttribute("from", scaleX + " " + scaleY);
animateScale.setAttribute("to", scaleXEnd + " " + scaleYEnd);
Element animateSkewX = exporter.createElement("animateTransform");
animateSkewX.setAttribute("dur", animationLengthStr);
animateSkewX.setAttribute("repeatCount", "indefinite");
animateSkewX.setAttribute("attributeName", attribute);
animateSkewX.setAttribute("type", "skewX");
animateSkewX.setAttribute("additive", "sum");
animateSkewX.setAttribute("from", Double.toString(skewX));
animateSkewX.setAttribute("to", Double.toString(skewXEnd));
element.appendChild(animateClear);
element.appendChild(animateTranslate);
element.appendChild(animateSkewY);
element.appendChild(animateScale);
element.appendChild(animateSkewX);*/
}
protected void populateGradientElement(Element gradient, int type, GRADRECORD[] gradientRecords, GRADRECORD[] gradientRecordsEnd, Matrix matrix, Matrix matrixEnd, 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) {
addMatrixAnimation(gradient, "gradientTransform", matrix, matrixEnd);
}
for (int i = 0; i < gradientRecords.length; i++) {
GRADRECORD record = gradientRecords[i];
GRADRECORD recordEnd = gradientRecordsEnd[i];
Element gradientEntry = exporter.createElement("stop");
gradientEntry.setAttribute("offset", Double.toString(record.ratio / 255.0));
gradientEntry.appendChild(createAnimateElement("offset", record.ratio / 255.0, recordEnd.ratio / 255.0));
RGB color = record.color;
RGB colorEnd = recordEnd.color;
//if(colors.get(i) != 0) {
gradientEntry.setAttribute("stop-color", color.toHexRGB());
gradientEntry.appendChild(createAnimateElement("stop-color", color.toHexRGB(), colorEnd.toHexRGB()));
//}
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
gradientEntry.setAttribute("stop-opacity", Float.toString(colorA.getAlphaFloat()));
}
RGBA colorAEnd = (RGBA) colorEnd;
gradientEntry.appendChild(createAnimateElement("stop-opacity", colorA.getAlphaFloat(), colorAEnd.getAlphaFloat()));
}
gradient.appendChild(gradientEntry);
}
}
protected double roundPixels400(double pixels) {
return Math.round(pixels * 10000) / 10000.0;
}
}
/*
* 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.exporters.morphshape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
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 org.w3c.dom.Element;
/**
*
* @author JPEXS, Claus Wahlers
*/
public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter {
protected Element path;
protected int id;
protected int lastPatternId;
private final Color defaultColor;
private final SWF swf;
private final SVGExporter exporter;
public SVGMorphShapeExporter(SWF swf, SHAPE shape, SHAPE endShape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) {
super(shape, endShape, colorTransform, zoom);
this.swf = swf;
this.id = id;
this.defaultColor = defaultColor;
this.exporter = exporter;
}
@Override
public void beginFill(RGB color, RGB colorEnd) {
if (color == null) {
color = new RGB(defaultColor == null ? Color.black : defaultColor);
}
if (colorEnd == null) {
colorEnd = new RGB(defaultColor == null ? Color.black : defaultColor);
}
finalizePath();
path.setAttribute("stroke", "none");
path.setAttribute("fill", color.toHexRGB());
path.setAttribute("fill-rule", "evenodd");
path.appendChild(createAnimateElement("fill", color.toHexRGB(), colorEnd.toHexRGB()));
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
path.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat()));
}
RGBA colorAEnd = (RGBA) colorEnd;
path.appendChild(createAnimateElement("fill-opacity", colorA.getAlphaFloat(), colorAEnd.getAlphaFloat()));
}
}
@Override
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, GRADRECORD[] gradientRecordsEnd, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio, float focalPointRatioEnd) {
finalizePath();
Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, gradientRecordsEnd, matrix, matrixEnd, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "none");
path.setAttribute("fill", "url(#gradient" + id + ")");
path.setAttribute("fill-rule", "evenodd");
exporter.addToDefs(gradient);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, Matrix matrixEnd, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImageCached();
if (img != null) {
if (colorTransform != null) {
colorTransform.apply(img);
}
int width = img.getWidth();
int height = img.getHeight();
lastPatternId++;
String patternId = "PatternID_" + id + "_" + lastPatternId;
ImageFormat format = image.getImageFormat();
byte[] imageData = Helper.readStream(image.getImageData());
String base64ImgData = Helper.byteArrayToBase64String(imageData);
path.setAttribute("style", "fill:url(#" + patternId + ")");
Element pattern = exporter.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) {
matrix = matrix.clone();
matrix.rotateSkew0 *= zoom / SWF.unitDivisor;
matrix.rotateSkew1 *= zoom / SWF.unitDivisor;
matrix.scaleX *= zoom / SWF.unitDivisor;
matrix.scaleY *= zoom / SWF.unitDivisor;
matrixEnd = matrixEnd.clone();
matrixEnd.rotateSkew0 *= zoom / SWF.unitDivisor;
matrixEnd.rotateSkew1 *= zoom / SWF.unitDivisor;
matrixEnd.scaleX *= zoom / SWF.unitDivisor;
matrixEnd.scaleY *= zoom / SWF.unitDivisor;
addMatrixAnimation(pattern, "patternTransform", matrix, matrixEnd);
}
Element imageElement = exporter.createElement("image");
imageElement.setAttribute("width", "" + width);
imageElement.setAttribute("height", "" + height);
imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData);
pattern.appendChild(imageElement);
exporter.addToGroup(pattern);
}
}
}
@Override
public void lineStyle(double thickness, double thicknessEnd, RGB color, RGB colorEnd, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
finalizePath();
thickness *= zoom / SWF.unitDivisor;
thicknessEnd *= zoom / SWF.unitDivisor;
path.setAttribute("fill", "none");
if (color != null) {
path.setAttribute("stroke", color.toHexRGB());
path.appendChild(createAnimateElement("stroke", color.toHexRGB(), colorEnd.toHexRGB()));
}
path.setAttribute("stroke-width", Double.toString(thickness == 0 ? 1 : thickness));
path.appendChild(createAnimateElement("stroke-width", thickness, thicknessEnd));
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
path.setAttribute("stroke-opacity", Float.toString(colorA.getAlphaFloat()));
}
RGBA colorAEnd = (RGBA) colorEnd;
path.appendChild(createAnimateElement("fill-opacity", colorA.getAlphaFloat(), colorAEnd.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 != 4f) {
path.setAttribute("stroke-miterlimit", Double.toString(miterLimit));
}
break;
}
}
@Override
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, GRADRECORD[] gradientRecordsEnd, Matrix matrix, Matrix matrixEnd, int spreadMethod, int interpolationMethod, float focalPointRatio, float focalPointRatioEnd) {
path.removeAttribute("stroke-opacity");
Element gradient = (type == FILLSTYLE.LINEAR_GRADIENT)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, gradientRecordsEnd, matrix, matrixEnd, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "url(#gradient" + id + ")");
path.setAttribute("fill", "none");
exporter.addToDefs(gradient);
}
private Element createAnimateElement(String attributeName, Object startValue, Object endValue) {
Element animate = exporter.createElement("animate");
animate.setAttribute("dur", "2s"); // todo
animate.setAttribute("repeatCount", "indefinite");
animate.setAttribute("attributeName", attributeName);
animate.setAttribute("values", startValue + ";" + endValue);
return animate;
}
@Override
protected void finalizePath() {
if (path != null && pathData != null && pathData.length() > 0) {
path.setAttribute("d", pathData.toString().trim());
path.appendChild(createAnimateElement("d", pathData.toString().trim(), pathDataEnd.toString().trim()));
exporter.addToGroup(path);
}
path = exporter.createElement("path");
super.finalizePath();
}
private void addMatrixAnimation(Element element, String attribute, Matrix matrix, Matrix matrixEnd) {
final int animationLength = 2; // todo
final String animationLengthStr = animationLength + "s";
element.setAttribute(attribute, matrix.getSvgTransformationString(SWF.unitDivisor / zoom, 1));
// QR decomposition
double translateX = roundPixels400(matrix.translateX * zoom / SWF.unitDivisor);
double translateY = roundPixels400(matrix.translateY * zoom / SWF.unitDivisor);
double a = matrix.scaleX;
double b = matrix.rotateSkew0;
double c = matrix.rotateSkew1;
double d = matrix.scaleY;
double r = Math.sqrt(a * a + b * b);
double det = a * d - b * c;
double rotate = roundPixels400(Math.signum(b) * Math.acos(a / r) * 180 / Math.PI);
double scaleX = roundPixels400(r);
double scaleY = roundPixels400(det / r);
double skewX = roundPixels400(Math.atan((a * c + b * d) / (r * r)) * 180 / Math.PI);
double translateXEnd = roundPixels400(matrixEnd.translateX * zoom / SWF.unitDivisor);
double translateYEnd = roundPixels400(matrixEnd.translateY * zoom / SWF.unitDivisor);
a = matrixEnd.scaleX;
b = matrixEnd.rotateSkew0;
c = matrixEnd.rotateSkew1;
d = matrixEnd.scaleY;
double rEnd = Math.sqrt(a * a + b * b);
double detEnd = a * d - b * c;
double rotateEnd = roundPixels400(Math.signum(b) * Math.acos(a / rEnd) * 180 / Math.PI);
double scaleXEnd = roundPixels400(rEnd);
double scaleYEnd = roundPixels400(detEnd / rEnd);
double skewXEnd = roundPixels400(Math.atan((a * c + b * d) / (rEnd * rEnd)) * 180 / Math.PI);
Element animateClear = exporter.createElement("animateTransform");
animateClear.setAttribute("dur", animationLengthStr);
animateClear.setAttribute("repeatCount", "indefinite");
animateClear.setAttribute("attributeName", attribute);
animateClear.setAttribute("type", "scale");
animateClear.setAttribute("additive", "replace");
animateClear.setAttribute("from", "1");
animateClear.setAttribute("to", "1");
Element animateTranslate = exporter.createElement("animateTransform");
animateTranslate.setAttribute("dur", animationLengthStr);
animateTranslate.setAttribute("repeatCount", "indefinite");
animateTranslate.setAttribute("attributeName", attribute);
animateTranslate.setAttribute("type", "translate");
animateTranslate.setAttribute("additive", "sum");
animateTranslate.setAttribute("from", translateX + " " + translateY);
animateTranslate.setAttribute("to", translateXEnd + " " + translateYEnd);
Element animateRotate = exporter.createElement("animateTransform");
animateRotate.setAttribute("dur", animationLengthStr);
animateRotate.setAttribute("repeatCount", "indefinite");
animateRotate.setAttribute("attributeName", attribute);
animateRotate.setAttribute("type", "rotate");
animateRotate.setAttribute("additive", "sum");
animateRotate.setAttribute("from", Double.toString(rotate));
animateRotate.setAttribute("to", Double.toString(rotateEnd));
Element animateScale = exporter.createElement("animateTransform");
animateScale.setAttribute("dur", animationLengthStr);
animateScale.setAttribute("repeatCount", "indefinite");
animateScale.setAttribute("attributeName", attribute);
animateScale.setAttribute("type", "scale");
animateScale.setAttribute("additive", "sum");
animateScale.setAttribute("from", scaleX + " " + scaleY);
animateScale.setAttribute("to", scaleXEnd + " " + scaleYEnd);
Element animateSkewX = exporter.createElement("animateTransform");
animateSkewX.setAttribute("dur", animationLengthStr);
animateSkewX.setAttribute("repeatCount", "indefinite");
animateSkewX.setAttribute("attributeName", attribute);
animateSkewX.setAttribute("type", "skewX");
animateSkewX.setAttribute("additive", "sum");
animateSkewX.setAttribute("from", Double.toString(skewX));
animateSkewX.setAttribute("to", Double.toString(skewXEnd));
element.appendChild(animateClear);
element.appendChild(animateTranslate);
element.appendChild(animateRotate);
element.appendChild(animateScale);
element.appendChild(animateSkewX);
/*
// LDU decomposition
double translateX = roundPixels400(matrix.translateX * zoom / SWF.unitDivisor);
double translateY = roundPixels400(matrix.translateY * zoom / SWF.unitDivisor);
double a = matrix.scaleX;
double b = matrix.rotateSkew0;
double c = matrix.rotateSkew1;
double d = matrix.scaleY;
double det = a * d - b * c;
double skewY = roundPixels400(Math.atan2(b, a) * 180 / Math.PI);
double scaleX = roundPixels400(a);
double scaleY = roundPixels400(det / a);
double skewX = roundPixels400(Math.atan2(c, a) * 180 / Math.PI);
double translateXEnd = roundPixels400(matrixEnd.translateX * zoom / SWF.unitDivisor);
double translateYEnd = roundPixels400(matrixEnd.translateY * zoom / SWF.unitDivisor);
a = matrixEnd.scaleX;
b = matrixEnd.rotateSkew0;
c = matrixEnd.rotateSkew1;
d = matrixEnd.scaleY;
double detEnd = a * d - b * c;
double skewYEnd = roundPixels400(Math.atan2(b, a) * 180 / Math.PI);
double scaleXEnd = roundPixels400(a);
double scaleYEnd = roundPixels400(detEnd / a);
double skewXEnd = roundPixels400(Math.atan2(c, a) * 180 / Math.PI);
Element animateClear = exporter.createElement("animateTransform");
animateClear.setAttribute("dur", animationLengthStr);
animateClear.setAttribute("repeatCount", "indefinite");
animateClear.setAttribute("attributeName", attribute);
animateClear.setAttribute("type", "scale");
animateClear.setAttribute("additive", "replace");
animateClear.setAttribute("from", "1");
animateClear.setAttribute("to", "1");
Element animateTranslate = exporter.createElement("animateTransform");
animateTranslate.setAttribute("dur", animationLengthStr);
animateTranslate.setAttribute("repeatCount", "indefinite");
animateTranslate.setAttribute("attributeName", attribute);
animateTranslate.setAttribute("type", "translate");
animateTranslate.setAttribute("additive", "sum");
animateTranslate.setAttribute("from", translateX + " " + translateY);
animateTranslate.setAttribute("to", translateXEnd + " " + translateYEnd);
Element animateSkewY = exporter.createElement("animateTransform");
animateSkewY.setAttribute("dur", animationLengthStr);
animateSkewY.setAttribute("repeatCount", "indefinite");
animateSkewY.setAttribute("attributeName", attribute);
animateSkewY.setAttribute("type", "skewY");
animateSkewY.setAttribute("additive", "sum");
animateSkewY.setAttribute("from", Double.toString(skewY));
animateSkewY.setAttribute("to", Double.toString(skewYEnd));
Element animateScale = exporter.createElement("animateTransform");
animateScale.setAttribute("dur", animationLengthStr);
animateScale.setAttribute("repeatCount", "indefinite");
animateScale.setAttribute("attributeName", attribute);
animateScale.setAttribute("type", "scale");
animateScale.setAttribute("additive", "sum");
animateScale.setAttribute("from", scaleX + " " + scaleY);
animateScale.setAttribute("to", scaleXEnd + " " + scaleYEnd);
Element animateSkewX = exporter.createElement("animateTransform");
animateSkewX.setAttribute("dur", animationLengthStr);
animateSkewX.setAttribute("repeatCount", "indefinite");
animateSkewX.setAttribute("attributeName", attribute);
animateSkewX.setAttribute("type", "skewX");
animateSkewX.setAttribute("additive", "sum");
animateSkewX.setAttribute("from", Double.toString(skewX));
animateSkewX.setAttribute("to", Double.toString(skewXEnd));
element.appendChild(animateClear);
element.appendChild(animateTranslate);
element.appendChild(animateSkewY);
element.appendChild(animateScale);
element.appendChild(animateSkewX);*/
}
protected void populateGradientElement(Element gradient, int type, GRADRECORD[] gradientRecords, GRADRECORD[] gradientRecordsEnd, Matrix matrix, Matrix matrixEnd, 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) {
addMatrixAnimation(gradient, "gradientTransform", matrix, matrixEnd);
}
for (int i = 0; i < gradientRecords.length; i++) {
GRADRECORD record = gradientRecords[i];
GRADRECORD recordEnd = gradientRecordsEnd[i];
Element gradientEntry = exporter.createElement("stop");
gradientEntry.setAttribute("offset", Double.toString(record.ratio / 255.0));
gradientEntry.appendChild(createAnimateElement("offset", record.ratio / 255.0, recordEnd.ratio / 255.0));
RGB color = record.color;
RGB colorEnd = recordEnd.color;
//if(colors.get(i) != 0) {
gradientEntry.setAttribute("stop-color", color.toHexRGB());
gradientEntry.appendChild(createAnimateElement("stop-color", color.toHexRGB(), colorEnd.toHexRGB()));
//}
if (color instanceof RGBA) {
RGBA colorA = (RGBA) color;
if (colorA.alpha != 255) {
gradientEntry.setAttribute("stop-opacity", Float.toString(colorA.getAlphaFloat()));
}
RGBA colorAEnd = (RGBA) colorEnd;
gradientEntry.appendChild(createAnimateElement("stop-opacity", colorA.getAlphaFloat(), colorAEnd.getAlphaFloat()));
}
gradient.appendChild(gradientEntry);
}
}
protected double roundPixels400(double pixels) {
return Math.round(pixels * 10000) / 10000.0;
}
}

View File

@@ -1,264 +1,264 @@
/*
* 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.exporters.shape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
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 org.w3c.dom.Element;
/**
*
* @author JPEXS, Claus Wahlers
*/
public class SVGShapeExporter extends DefaultSVGShapeExporter {
protected Element path;
protected int id;
protected int lastPatternId;
private final Color defaultColor;
private final SWF swf;
private final SVGExporter exporter;
public SVGShapeExporter(SWF swf, SHAPE shape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) {
super(swf, shape, colorTransform, zoom);
this.swf = swf;
this.id = id;
this.defaultColor = defaultColor;
this.exporter = exporter;
}
@Override
public void beginFill(RGB color) {
if (color == null && defaultColor != null) {
color = new RGB(defaultColor);
}
finalizePath();
path.setAttribute("stroke", "none");
if (color != null) {
path.setAttribute("fill", color.toHexRGB());
}
path.setAttribute("fill-rule", "evenodd");
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)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
String gradientId = "gradient" + id;
gradient.setAttribute("id", gradientId);
path.setAttribute("stroke", "none");
path.setAttribute("fill", "url(#" + gradientId + ")");
path.setAttribute("fill-rule", "evenodd");
exporter.addToDefs(gradient);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImageCached();
if (img != null) {
if (colorTransform != null) {
colorTransform.apply(img);
}
int width = img.getWidth();
int height = img.getHeight();
lastPatternId++;
String patternId = "PatternID_" + id + "_" + lastPatternId;
ImageFormat format = image.getImageFormat();
byte[] imageData = Helper.readStream(image.getImageData());
String base64ImgData = Helper.byteArrayToBase64String(imageData);
path.setAttribute("style", "fill:url(#" + patternId + ")");
Element pattern = exporter.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) {
pattern.setAttribute("patternTransform", matrix.getSvgTransformationString(SWF.unitDivisor / zoom, SWF.unitDivisor / zoom));
}
Element imageElement = exporter.createElement("image");
imageElement.setAttribute("width", "" + width);
imageElement.setAttribute("height", "" + height);
imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData);
pattern.appendChild(imageElement);
exporter.addToGroup(pattern);
return;
}
}
path.setAttribute("fill", "#ff0000");
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
finalizePath();
thickness *= zoom / SWF.unitDivisor;
path.setAttribute("fill", "none");
if (color != null) {
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 != 4f) {
path.setAttribute("stroke-miterlimit", Double.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)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "url(#gradient" + id + ")");
path.setAttribute("fill", "none");
exporter.addToDefs(gradient);
}
@Override
protected void finalizePath() {
if (path != null && pathData != null && pathData.length() > 0) {
path.setAttribute("d", pathData.toString().trim());
exporter.addToGroup(path);
}
path = exporter.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) {
gradient.setAttribute("gradientTransform", matrix.getSvgTransformationString(SWF.unitDivisor / zoom, 1));
}
for (int i = 0; i < gradientRecords.length; i++) {
GRADRECORD record = gradientRecords[i];
Element gradientEntry = exporter.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);
}
}
}
/*
* 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.exporters.shape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
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 org.w3c.dom.Element;
/**
*
* @author JPEXS, Claus Wahlers
*/
public class SVGShapeExporter extends DefaultSVGShapeExporter {
protected Element path;
protected int id;
protected int lastPatternId;
private final Color defaultColor;
private final SWF swf;
private final SVGExporter exporter;
public SVGShapeExporter(SWF swf, SHAPE shape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) {
super(swf, shape, colorTransform, zoom);
this.swf = swf;
this.id = id;
this.defaultColor = defaultColor;
this.exporter = exporter;
}
@Override
public void beginFill(RGB color) {
if (color == null && defaultColor != null) {
color = new RGB(defaultColor);
}
finalizePath();
path.setAttribute("stroke", "none");
if (color != null) {
path.setAttribute("fill", color.toHexRGB());
}
path.setAttribute("fill-rule", "evenodd");
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)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
String gradientId = "gradient" + id;
gradient.setAttribute("id", gradientId);
path.setAttribute("stroke", "none");
path.setAttribute("fill", "url(#" + gradientId + ")");
path.setAttribute("fill-rule", "evenodd");
exporter.addToDefs(gradient);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImageCached();
if (img != null) {
if (colorTransform != null) {
colorTransform.apply(img);
}
int width = img.getWidth();
int height = img.getHeight();
lastPatternId++;
String patternId = "PatternID_" + id + "_" + lastPatternId;
ImageFormat format = image.getImageFormat();
byte[] imageData = Helper.readStream(image.getImageData());
String base64ImgData = Helper.byteArrayToBase64String(imageData);
path.setAttribute("style", "fill:url(#" + patternId + ")");
Element pattern = exporter.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) {
pattern.setAttribute("patternTransform", matrix.getSvgTransformationString(SWF.unitDivisor / zoom, SWF.unitDivisor / zoom));
}
Element imageElement = exporter.createElement("image");
imageElement.setAttribute("width", "" + width);
imageElement.setAttribute("height", "" + height);
imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData);
pattern.appendChild(imageElement);
exporter.addToGroup(pattern);
return;
}
}
path.setAttribute("fill", "#ff0000");
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
finalizePath();
thickness *= zoom / SWF.unitDivisor;
path.setAttribute("fill", "none");
if (color != null) {
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 != 4f) {
path.setAttribute("stroke-miterlimit", Double.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)
? exporter.createElement("linearGradient")
: exporter.createElement("radialGradient");
populateGradientElement(gradient, type, gradientRecords, matrix, spreadMethod, interpolationMethod, focalPointRatio);
int id = exporter.gradients.indexOf(gradient);
if (id < 0) {
// todo: filter same gradients
id = exporter.gradients.size();
exporter.gradients.add(gradient);
}
gradient.setAttribute("id", "gradient" + id);
path.setAttribute("stroke", "url(#gradient" + id + ")");
path.setAttribute("fill", "none");
exporter.addToDefs(gradient);
}
@Override
protected void finalizePath() {
if (path != null && pathData != null && pathData.length() > 0) {
path.setAttribute("d", pathData.toString().trim());
exporter.addToGroup(path);
}
path = exporter.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) {
gradient.setAttribute("gradientTransform", matrix.getSvgTransformationString(SWF.unitDivisor / zoom, 1));
}
for (int i = 0; i < gradientRecords.length; i++) {
GRADRECORD record = gradientRecords[i];
Element gradientEntry = exporter.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);
}
}
}

View File

@@ -1,281 +1,281 @@
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.TagInfo;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
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.SerializableImage;
import java.awt.Dimension;
import java.awt.Shape;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Set;
/**
*
* @author JPEXS
*/
public abstract class ImageTag extends DrawableTag {
@SWFType(BasicType.UI16)
public int characterID;
protected SerializableImage cachedImage;
public ImageTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract InputStream getOriginalImageData();
protected abstract SerializableImage getImage();
public abstract Dimension getImageDimension();
public abstract void setImage(byte[] data) throws IOException;
public abstract ImageFormat getImageFormat();
public abstract ImageFormat getOriginalImageFormat();
public boolean importSupported() {
return true;
}
public static ImageFormat getImageFormat(byte[] data) {
return getImageFormat(new ByteArrayRange(data));
}
public static ImageFormat getImageFormat(ByteArrayRange data) {
if (hasErrorHeader(data)) {
return ImageFormat.JPEG;
}
if (data.getLength() > 2 && ((data.get(0) & 0xff) == 0xff) && ((data.get(1) & 0xff) == 0xd8)) {
return ImageFormat.JPEG;
}
if (data.getLength() > 6 && ((data.get(0) & 0xff) == 0x47) && ((data.get(1) & 0xff) == 0x49) && ((data.get(2) & 0xff) == 0x46) && ((data.get(3) & 0xff) == 0x38) && ((data.get(4) & 0xff) == 0x39) && ((data.get(5) & 0xff) == 0x61)) {
return ImageFormat.GIF;
}
if (data.getLength() > 8 && ((data.get(0) & 0xff) == 0x89) && ((data.get(1) & 0xff) == 0x50) && ((data.get(2) & 0xff) == 0x4e) && ((data.get(3) & 0xff) == 0x47) && ((data.get(4) & 0xff) == 0x0d) && ((data.get(5) & 0xff) == 0x0a) && ((data.get(6) & 0xff) == 0x1a) && ((data.get(7) & 0xff) == 0x0a)) {
return ImageFormat.PNG;
}
return ImageFormat.UNKNOWN;
}
public SerializableImage getImageCached() {
if (cachedImage != null) {
return cachedImage;
}
SerializableImage image = getImage();
if (Configuration.cacheImages.get()) {
cachedImage = image;
}
return image;
}
public InputStream getImageData() {
InputStream is = getOriginalImageData();
if (is != null) {
return is;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageHelper.write(getImage().getBufferedImage(), getImageFormat(), baos);
return new ByteArrayInputStream(baos.toByteArray());
}
public static boolean hasErrorHeader(byte[] data) {
return hasErrorHeader(new ByteArrayRange(data));
}
public static boolean hasErrorHeader(ByteArrayRange data) {
if (data.getLength() > 4) {
if ((data.get(0) & 0xff) == 0xff && (data.get(1) & 0xff) == 0xd9
&& (data.get(2) & 0xff) == 0xff && (data.get(3) & 0xff) == 0xd8) {
return true;
}
}
return false;
}
private SHAPEWITHSTYLE getShape() {
RECT rect = getRect();
return getShape(rect, false);
}
public SHAPEWITHSTYLE getShape(RECT rect, boolean fill) {
boolean translated = rect.Xmin != 0 || rect.Ymin != 0;
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = new FILLSTYLEARRAY();
shape.fillStyles.fillStyles = new FILLSTYLE[1];
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = Configuration.shapeImportUseNonSmoothedFill.get()
? FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP : FILLSTYLE.REPEATING_BITMAP;
fillStyle.bitmapId = getCharacterId();
MATRIX matrix = new MATRIX();
matrix.hasScale = true;
if (fill) {
RECT imageRect = getRect();
matrix.scaleX = (int) ((((long) SWF.unitDivisor) << 16) * rect.getWidth() / imageRect.getWidth());
matrix.scaleY = (int) ((((long) SWF.unitDivisor) << 16) * rect.getHeight() / imageRect.getHeight());
} else {
matrix.scaleX = ((int) SWF.unitDivisor) << 16;
matrix.scaleY = matrix.scaleX;
}
if (translated) {
matrix.translateX = rect.Xmin;
matrix.translateY = rect.Ymin;
}
fillStyle.bitmapMatrix = matrix;
shape.fillStyles.fillStyles[0] = fillStyle;
shape.lineStyles = new LINESTYLEARRAY();
shape.lineStyles.lineStyles = new LINESTYLE[0];
shape.shapeRecords = new ArrayList<>();
StyleChangeRecord style = new StyleChangeRecord();
style.stateFillStyle0 = true;
style.fillStyle0 = 1;
style.stateMoveTo = true;
if (translated) {
style.moveDeltaX = rect.Xmin;
style.moveDeltaY = rect.Ymin;
}
shape.shapeRecords.add(style);
StraightEdgeRecord top = new StraightEdgeRecord();
top.generalLineFlag = true;
top.deltaX = rect.getWidth();
StraightEdgeRecord right = new StraightEdgeRecord();
right.generalLineFlag = true;
right.deltaY = rect.getHeight();
StraightEdgeRecord bottom = new StraightEdgeRecord();
bottom.generalLineFlag = true;
bottom.deltaX = -rect.getWidth();
StraightEdgeRecord left = new StraightEdgeRecord();
left.generalLineFlag = true;
left.deltaY = -rect.getHeight();
shape.shapeRecords.add(top);
shape.shapeRecords.add(right);
shape.shapeRecords.add(bottom);
shape.shapeRecords.add(left);
shape.shapeRecords.add(new EndShapeRecord());
return shape;
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
}
@Override
public RECT getRect(Set<BoundedTag> added) {
Dimension dimension = getImageDimension();
int widthInTwips = (int) (dimension.getWidth() * SWF.unitDivisor);
int heightInTwips = (int) (dimension.getHeight() * SWF.unitDivisor);
return new RECT(0, widthInTwips, 0, heightInTwips);
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
return transformation.toTransform().createTransformedShape(getShape().getOutline(swf, stroked));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
BitmapExporter.export(swf, getShape(), null, image, transformation, strokeTransformation, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShape(), getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, getShape(), null, 0, 0);
cse.export();
result.append(cse.getShapeData());
}
@Override
public int getNumFrames() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
public void clearCache() {
cachedImage = null;
}
@Override
public void getTagInfo(TagInfo tagInfo) {
super.getTagInfo(tagInfo);
Dimension dimension = getImageDimension();
tagInfo.addInfo("general", "width", dimension.getWidth());
tagInfo.addInfo("general", "height", dimension.getHeight());
}
@Override
public int getCharacterId() {
return characterID;
}
@Override
public void setCharacterId(int characterId) {
this.characterID = characterId;
}
}
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.TagInfo;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
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.SerializableImage;
import java.awt.Dimension;
import java.awt.Shape;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Set;
/**
*
* @author JPEXS
*/
public abstract class ImageTag extends DrawableTag {
@SWFType(BasicType.UI16)
public int characterID;
protected SerializableImage cachedImage;
public ImageTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract InputStream getOriginalImageData();
protected abstract SerializableImage getImage();
public abstract Dimension getImageDimension();
public abstract void setImage(byte[] data) throws IOException;
public abstract ImageFormat getImageFormat();
public abstract ImageFormat getOriginalImageFormat();
public boolean importSupported() {
return true;
}
public static ImageFormat getImageFormat(byte[] data) {
return getImageFormat(new ByteArrayRange(data));
}
public static ImageFormat getImageFormat(ByteArrayRange data) {
if (hasErrorHeader(data)) {
return ImageFormat.JPEG;
}
if (data.getLength() > 2 && ((data.get(0) & 0xff) == 0xff) && ((data.get(1) & 0xff) == 0xd8)) {
return ImageFormat.JPEG;
}
if (data.getLength() > 6 && ((data.get(0) & 0xff) == 0x47) && ((data.get(1) & 0xff) == 0x49) && ((data.get(2) & 0xff) == 0x46) && ((data.get(3) & 0xff) == 0x38) && ((data.get(4) & 0xff) == 0x39) && ((data.get(5) & 0xff) == 0x61)) {
return ImageFormat.GIF;
}
if (data.getLength() > 8 && ((data.get(0) & 0xff) == 0x89) && ((data.get(1) & 0xff) == 0x50) && ((data.get(2) & 0xff) == 0x4e) && ((data.get(3) & 0xff) == 0x47) && ((data.get(4) & 0xff) == 0x0d) && ((data.get(5) & 0xff) == 0x0a) && ((data.get(6) & 0xff) == 0x1a) && ((data.get(7) & 0xff) == 0x0a)) {
return ImageFormat.PNG;
}
return ImageFormat.UNKNOWN;
}
public SerializableImage getImageCached() {
if (cachedImage != null) {
return cachedImage;
}
SerializableImage image = getImage();
if (Configuration.cacheImages.get()) {
cachedImage = image;
}
return image;
}
public InputStream getImageData() {
InputStream is = getOriginalImageData();
if (is != null) {
return is;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageHelper.write(getImage().getBufferedImage(), getImageFormat(), baos);
return new ByteArrayInputStream(baos.toByteArray());
}
public static boolean hasErrorHeader(byte[] data) {
return hasErrorHeader(new ByteArrayRange(data));
}
public static boolean hasErrorHeader(ByteArrayRange data) {
if (data.getLength() > 4) {
if ((data.get(0) & 0xff) == 0xff && (data.get(1) & 0xff) == 0xd9
&& (data.get(2) & 0xff) == 0xff && (data.get(3) & 0xff) == 0xd8) {
return true;
}
}
return false;
}
private SHAPEWITHSTYLE getShape() {
RECT rect = getRect();
return getShape(rect, false);
}
public SHAPEWITHSTYLE getShape(RECT rect, boolean fill) {
boolean translated = rect.Xmin != 0 || rect.Ymin != 0;
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = new FILLSTYLEARRAY();
shape.fillStyles.fillStyles = new FILLSTYLE[1];
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = Configuration.shapeImportUseNonSmoothedFill.get()
? FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP : FILLSTYLE.REPEATING_BITMAP;
fillStyle.bitmapId = getCharacterId();
MATRIX matrix = new MATRIX();
matrix.hasScale = true;
if (fill) {
RECT imageRect = getRect();
matrix.scaleX = (int) ((((long) SWF.unitDivisor) << 16) * rect.getWidth() / imageRect.getWidth());
matrix.scaleY = (int) ((((long) SWF.unitDivisor) << 16) * rect.getHeight() / imageRect.getHeight());
} else {
matrix.scaleX = ((int) SWF.unitDivisor) << 16;
matrix.scaleY = matrix.scaleX;
}
if (translated) {
matrix.translateX = rect.Xmin;
matrix.translateY = rect.Ymin;
}
fillStyle.bitmapMatrix = matrix;
shape.fillStyles.fillStyles[0] = fillStyle;
shape.lineStyles = new LINESTYLEARRAY();
shape.lineStyles.lineStyles = new LINESTYLE[0];
shape.shapeRecords = new ArrayList<>();
StyleChangeRecord style = new StyleChangeRecord();
style.stateFillStyle0 = true;
style.fillStyle0 = 1;
style.stateMoveTo = true;
if (translated) {
style.moveDeltaX = rect.Xmin;
style.moveDeltaY = rect.Ymin;
}
shape.shapeRecords.add(style);
StraightEdgeRecord top = new StraightEdgeRecord();
top.generalLineFlag = true;
top.deltaX = rect.getWidth();
StraightEdgeRecord right = new StraightEdgeRecord();
right.generalLineFlag = true;
right.deltaY = rect.getHeight();
StraightEdgeRecord bottom = new StraightEdgeRecord();
bottom.generalLineFlag = true;
bottom.deltaX = -rect.getWidth();
StraightEdgeRecord left = new StraightEdgeRecord();
left.generalLineFlag = true;
left.deltaY = -rect.getHeight();
shape.shapeRecords.add(top);
shape.shapeRecords.add(right);
shape.shapeRecords.add(bottom);
shape.shapeRecords.add(left);
shape.shapeRecords.add(new EndShapeRecord());
return shape;
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
}
@Override
public RECT getRect(Set<BoundedTag> added) {
Dimension dimension = getImageDimension();
int widthInTwips = (int) (dimension.getWidth() * SWF.unitDivisor);
int heightInTwips = (int) (dimension.getHeight() * SWF.unitDivisor);
return new RECT(0, widthInTwips, 0, heightInTwips);
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
return transformation.toTransform().createTransformedShape(getShape().getOutline(swf, stroked));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
BitmapExporter.export(swf, getShape(), null, image, transformation, strokeTransformation, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShape(), getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, getShape(), null, 0, 0);
cse.export();
result.append(cse.getShapeData());
}
@Override
public int getNumFrames() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
public void clearCache() {
cachedImage = null;
}
@Override
public void getTagInfo(TagInfo tagInfo) {
super.getTagInfo(tagInfo);
Dimension dimension = getImageDimension();
tagInfo.addInfo("general", "width", dimension.getWidth());
tagInfo.addInfo("general", "height", dimension.getHeight());
}
@Override
public int getCharacterId() {
return characterID;
}
@Override
public void setCharacterId(int characterId) {
this.characterID = characterId;
}
}

View File

@@ -1,327 +1,327 @@
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
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.SerializableImage;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author JPEXS
*/
public abstract class MorphShapeTag extends DrawableTag {
public static final int MAX_RATIO = 65535;
@SWFType(BasicType.UI16)
public int characterId;
public RECT startBounds;
public RECT endBounds;
public MORPHFILLSTYLEARRAY morphFillStyles;
public MORPHLINESTYLEARRAY morphLineStyles;
public SHAPE startEdges;
public SHAPE endEdges;
public MorphShapeTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract int getShapeNum();
@Override
public RECT getRect() {
return getRect(new HashSet<>());
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
morphFillStyles.getNeededCharacters(needed);
startEdges.getNeededCharacters(needed);
endEdges.getNeededCharacters(needed);
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
modified |= morphFillStyles.replaceCharacter(oldCharacterId, newCharacterId);
modified |= startEdges.replaceCharacter(oldCharacterId, newCharacterId);
modified |= endEdges.replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
modified |= morphFillStyles.removeCharacter(characterId);
modified |= startEdges.removeCharacter(characterId);
modified |= endEdges.removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public int getCharacterId() {
return characterId;
}
@Override
public void setCharacterId(int characterId) {
this.characterId = characterId;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
RECT rect = new RECT();
rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin);
rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin);
rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax);
rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax);
return rect;
}
public RECT getStartBounds() {
return startBounds;
}
public RECT getEndBounds() {
return endBounds;
}
public MORPHFILLSTYLEARRAY getFillStyles() {
return morphFillStyles;
}
public MORPHLINESTYLEARRAY getLineStyles() {
return morphLineStyles;
}
public SHAPE getStartEdges() {
return startEdges;
}
public SHAPE getEndEdges() {
return endEdges;
}
public SHAPEWITHSTYLE getShapeAtRatio(int ratio) {
List<SHAPERECORD> finalRecords = new ArrayList<>();
FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio);
LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio);
int startPosX = 0, startPosY = 0;
int endPosX = 0, endPosY = 0;
int posX = 0, posY = 0;
for (int startIndex = 0, endIndex = 0;
startIndex < startEdges.shapeRecords.size()
&& endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) {
SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex);
SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex);
if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) {
StyleChangeRecord scr1;
if (edge1 instanceof StyleChangeRecord) {
scr1 = (StyleChangeRecord) edge1;
if (scr1.stateMoveTo) {
startPosX = scr1.moveDeltaX;
startPosY = scr1.moveDeltaY;
}
} else {
scr1 = new StyleChangeRecord();
startIndex--;
}
StyleChangeRecord scr2;
if (edge2 instanceof StyleChangeRecord) {
scr2 = (StyleChangeRecord) edge2;
if (scr2.stateMoveTo) {
endPosX = scr2.moveDeltaX;
endPosY = scr2.moveDeltaY;
}
} else {
scr2 = new StyleChangeRecord();
endIndex--;
}
StyleChangeRecord scr = scr1.clone();
if (scr1.stateMoveTo || scr2.stateMoveTo) {
scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / MAX_RATIO;
scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / MAX_RATIO;
scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY;
}
finalRecords.add(scr);
continue;
}
if (edge1 instanceof EndShapeRecord) {
finalRecords.add(edge1);
break;
}
if (edge2 instanceof EndShapeRecord) {
finalRecords.add(edge2);
break;
}
if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer1 = null;
if (edge1 instanceof CurvedEdgeRecord) {
cer1 = (CurvedEdgeRecord) edge1;
} else if (edge1 instanceof StraightEdgeRecord) {
cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1);
}
CurvedEdgeRecord cer2 = null;
if (edge2 instanceof CurvedEdgeRecord) {
cer2 = (CurvedEdgeRecord) edge2;
} else if (edge2 instanceof StraightEdgeRecord) {
cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2);
}
if ((cer2 == null) || (cer1 == null)) {
continue;
}
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / MAX_RATIO;
cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / MAX_RATIO;
cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / MAX_RATIO;
cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / MAX_RATIO;
startPosX += cer1.controlDeltaX + cer1.anchorDeltaX;
startPosY += cer1.controlDeltaY + cer1.anchorDeltaY;
endPosX += cer2.controlDeltaX + cer2.anchorDeltaX;
endPosY += cer2.controlDeltaY + cer2.anchorDeltaY;
posX += cer.controlDeltaX + cer.anchorDeltaX;
posY += cer.controlDeltaY + cer.anchorDeltaY;
finalRecords.add(cer);
} else {
StraightEdgeRecord ser1 = null;
if (edge1 instanceof StraightEdgeRecord) {
ser1 = (StraightEdgeRecord) edge1;
}
StraightEdgeRecord ser2 = null;
if (edge2 instanceof StraightEdgeRecord) {
ser2 = (StraightEdgeRecord) edge2;
}
if ((ser2 == null) || (ser1 == null)) {
continue;
}
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.generalLineFlag = true;
ser.vertLineFlag = false;
ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / MAX_RATIO;
ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / MAX_RATIO;
startPosX += ser1.deltaX;
startPosY += ser1.deltaY;
endPosX += ser2.deltaX;
endPosY += ser2.deltaY;
posX += ser.deltaX;
posY += ser.deltaX;
finalRecords.add(ser);
}
}
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = fillStyles;
shape.lineStyles = lineStyles;
shape.shapeRecords = finalRecords;
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);
// morphShape using shapeNum=3, morphShape2 using shapeNum=4
// todo: Currently the generated image is not cached, because the cache
// key contains the hashCode of the finalRecord object, and it is always
// recreated
BitmapExporter.export(swf, shape, null, image, transformation, strokeTransformation, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
if (ratio == -2) {
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
} else {
SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio);
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
}
}
@Override
public int getNumFrames() {
return 65536;
}
@Override
public boolean isSingleFrame() {
// Morpshape is a single frame specified with the ratio
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(swf, stroked));
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), null, unitDivisor, 0, 0);
cmse.export();
result.append(cmse.getShapeData());
}
}
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
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.SerializableImage;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author JPEXS
*/
public abstract class MorphShapeTag extends DrawableTag {
public static final int MAX_RATIO = 65535;
@SWFType(BasicType.UI16)
public int characterId;
public RECT startBounds;
public RECT endBounds;
public MORPHFILLSTYLEARRAY morphFillStyles;
public MORPHLINESTYLEARRAY morphLineStyles;
public SHAPE startEdges;
public SHAPE endEdges;
public MorphShapeTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract int getShapeNum();
@Override
public RECT getRect() {
return getRect(new HashSet<>());
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
morphFillStyles.getNeededCharacters(needed);
startEdges.getNeededCharacters(needed);
endEdges.getNeededCharacters(needed);
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
modified |= morphFillStyles.replaceCharacter(oldCharacterId, newCharacterId);
modified |= startEdges.replaceCharacter(oldCharacterId, newCharacterId);
modified |= endEdges.replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
modified |= morphFillStyles.removeCharacter(characterId);
modified |= startEdges.removeCharacter(characterId);
modified |= endEdges.removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public int getCharacterId() {
return characterId;
}
@Override
public void setCharacterId(int characterId) {
this.characterId = characterId;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
RECT rect = new RECT();
rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin);
rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin);
rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax);
rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax);
return rect;
}
public RECT getStartBounds() {
return startBounds;
}
public RECT getEndBounds() {
return endBounds;
}
public MORPHFILLSTYLEARRAY getFillStyles() {
return morphFillStyles;
}
public MORPHLINESTYLEARRAY getLineStyles() {
return morphLineStyles;
}
public SHAPE getStartEdges() {
return startEdges;
}
public SHAPE getEndEdges() {
return endEdges;
}
public SHAPEWITHSTYLE getShapeAtRatio(int ratio) {
List<SHAPERECORD> finalRecords = new ArrayList<>();
FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio);
LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio);
int startPosX = 0, startPosY = 0;
int endPosX = 0, endPosY = 0;
int posX = 0, posY = 0;
for (int startIndex = 0, endIndex = 0;
startIndex < startEdges.shapeRecords.size()
&& endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) {
SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex);
SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex);
if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) {
StyleChangeRecord scr1;
if (edge1 instanceof StyleChangeRecord) {
scr1 = (StyleChangeRecord) edge1;
if (scr1.stateMoveTo) {
startPosX = scr1.moveDeltaX;
startPosY = scr1.moveDeltaY;
}
} else {
scr1 = new StyleChangeRecord();
startIndex--;
}
StyleChangeRecord scr2;
if (edge2 instanceof StyleChangeRecord) {
scr2 = (StyleChangeRecord) edge2;
if (scr2.stateMoveTo) {
endPosX = scr2.moveDeltaX;
endPosY = scr2.moveDeltaY;
}
} else {
scr2 = new StyleChangeRecord();
endIndex--;
}
StyleChangeRecord scr = scr1.clone();
if (scr1.stateMoveTo || scr2.stateMoveTo) {
scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / MAX_RATIO;
scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / MAX_RATIO;
scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY;
}
finalRecords.add(scr);
continue;
}
if (edge1 instanceof EndShapeRecord) {
finalRecords.add(edge1);
break;
}
if (edge2 instanceof EndShapeRecord) {
finalRecords.add(edge2);
break;
}
if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer1 = null;
if (edge1 instanceof CurvedEdgeRecord) {
cer1 = (CurvedEdgeRecord) edge1;
} else if (edge1 instanceof StraightEdgeRecord) {
cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1);
}
CurvedEdgeRecord cer2 = null;
if (edge2 instanceof CurvedEdgeRecord) {
cer2 = (CurvedEdgeRecord) edge2;
} else if (edge2 instanceof StraightEdgeRecord) {
cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2);
}
if ((cer2 == null) || (cer1 == null)) {
continue;
}
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / MAX_RATIO;
cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / MAX_RATIO;
cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / MAX_RATIO;
cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / MAX_RATIO;
startPosX += cer1.controlDeltaX + cer1.anchorDeltaX;
startPosY += cer1.controlDeltaY + cer1.anchorDeltaY;
endPosX += cer2.controlDeltaX + cer2.anchorDeltaX;
endPosY += cer2.controlDeltaY + cer2.anchorDeltaY;
posX += cer.controlDeltaX + cer.anchorDeltaX;
posY += cer.controlDeltaY + cer.anchorDeltaY;
finalRecords.add(cer);
} else {
StraightEdgeRecord ser1 = null;
if (edge1 instanceof StraightEdgeRecord) {
ser1 = (StraightEdgeRecord) edge1;
}
StraightEdgeRecord ser2 = null;
if (edge2 instanceof StraightEdgeRecord) {
ser2 = (StraightEdgeRecord) edge2;
}
if ((ser2 == null) || (ser1 == null)) {
continue;
}
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.generalLineFlag = true;
ser.vertLineFlag = false;
ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / MAX_RATIO;
ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / MAX_RATIO;
startPosX += ser1.deltaX;
startPosY += ser1.deltaY;
endPosX += ser2.deltaX;
endPosY += ser2.deltaY;
posX += ser.deltaX;
posY += ser.deltaX;
finalRecords.add(ser);
}
}
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = fillStyles;
shape.lineStyles = lineStyles;
shape.shapeRecords = finalRecords;
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);
// morphShape using shapeNum=3, morphShape2 using shapeNum=4
// todo: Currently the generated image is not cached, because the cache
// key contains the hashCode of the finalRecord object, and it is always
// recreated
BitmapExporter.export(swf, shape, null, image, transformation, strokeTransformation, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
if (ratio == -2) {
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
} else {
SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio);
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
}
}
@Override
public int getNumFrames() {
return 65536;
}
@Override
public boolean isSingleFrame() {
// Morpshape is a single frame specified with the ratio
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(swf, stroked));
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), null, unitDivisor, 0, 0);
cmse.export();
result.append(cmse.getShapeData());
}
}

View File

@@ -1,213 +1,213 @@
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.PathExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.helpers.LazyObject;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public abstract class ShapeTag extends DrawableTag implements LazyObject {
@SWFType(BasicType.UI16)
public int shapeId;
public RECT shapeBounds;
public SHAPEWITHSTYLE shapes;
protected ByteArrayRange shapeData;
private final int markerSize = 10;
public ShapeTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
@Override
public void load() {
getShapes();
}
public abstract int getShapeNum();
public SHAPEWITHSTYLE getShapes() {
if (shapes == null && shapeData != null) {
try {
SWFInputStream sis = new SWFInputStream(swf, shapeData.getArray(), 0, shapeData.getPos() + shapeData.getLength());
sis.seek(shapeData.getPos());
shapes = sis.readSHAPEWITHSTYLE(getShapeNum(), false, "shapes");
shapeData = null; // not needed anymore, give it to GC
} catch (IOException ex) {
Logger.getLogger(ShapeTag.class.getName()).log(Level.SEVERE, null, ex);
}
}
return shapes;
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
SHAPEWITHSTYLE shapes = getShapes();
if (shapes != null) {
getShapes().getNeededCharacters(needed);
}
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = getShapes().replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = getShapes().removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return shapeBounds;
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
return transformation.toTransform().createTransformedShape(getShapes().getOutline(swf, stroked));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
BitmapExporter.export(swf, getShapes(), null, image, transformation, strokeTransformation, colorTransform);
if (Configuration._debugMode.get()) { // show control points
List<GeneralPath> paths = PathExporter.export(swf, getShapes());
double[] coords = new double[6];
AffineTransform at = transformation.toTransform();
at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
// get the graphics from the inner image object, because it creates a new Graphics object
Graphics2D graphics = (Graphics2D) image.getBufferedImage().getGraphics();
graphics.setPaint(Color.black);
for (GeneralPath path : paths) {
PathIterator iterator = path.getPathIterator(at);
while (!iterator.isDone()) {
int type = iterator.currentSegment(coords);
double x = coords[0];
double y = coords[1];
switch (type) {
case PathIterator.SEG_MOVETO:
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
break;
case PathIterator.SEG_LINETO:
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
break;
case PathIterator.SEG_QUADTO:
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
x = coords[2];
y = coords[3];
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
break;
case PathIterator.SEG_CUBICTO:
System.out.print("CUBICTO NOT SUPPORTED. ");
break;
case PathIterator.SEG_CLOSE:
System.out.print("CLOSE NOT SUPPORTED. ");
break;
}
iterator.next();
}
}
}
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShapes(), getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, getShapes(), null, 0, 0);
cse.export();
result.append(cse.getShapeData());
}
@Override
public int getNumFrames() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public int getCharacterId() {
return shapeId;
}
@Override
public void setCharacterId(int characterId) {
this.shapeId = characterId;
}
}
/*
* 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.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.PathExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.helpers.LazyObject;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public abstract class ShapeTag extends DrawableTag implements LazyObject {
@SWFType(BasicType.UI16)
public int shapeId;
public RECT shapeBounds;
public SHAPEWITHSTYLE shapes;
protected ByteArrayRange shapeData;
private final int markerSize = 10;
public ShapeTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
@Override
public void load() {
getShapes();
}
public abstract int getShapeNum();
public SHAPEWITHSTYLE getShapes() {
if (shapes == null && shapeData != null) {
try {
SWFInputStream sis = new SWFInputStream(swf, shapeData.getArray(), 0, shapeData.getPos() + shapeData.getLength());
sis.seek(shapeData.getPos());
shapes = sis.readSHAPEWITHSTYLE(getShapeNum(), false, "shapes");
shapeData = null; // not needed anymore, give it to GC
} catch (IOException ex) {
Logger.getLogger(ShapeTag.class.getName()).log(Level.SEVERE, null, ex);
}
}
return shapes;
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
SHAPEWITHSTYLE shapes = getShapes();
if (shapes != null) {
getShapes().getNeededCharacters(needed);
}
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = getShapes().replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = getShapes().removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return shapeBounds;
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
return transformation.toTransform().createTransformedShape(getShapes().getOutline(swf, stroked));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
BitmapExporter.export(swf, getShapes(), null, image, transformation, strokeTransformation, colorTransform);
if (Configuration._debugMode.get()) { // show control points
List<GeneralPath> paths = PathExporter.export(swf, getShapes());
double[] coords = new double[6];
AffineTransform at = transformation.toTransform();
at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
// get the graphics from the inner image object, because it creates a new Graphics object
Graphics2D graphics = (Graphics2D) image.getBufferedImage().getGraphics();
graphics.setPaint(Color.black);
for (GeneralPath path : paths) {
PathIterator iterator = path.getPathIterator(at);
while (!iterator.isDone()) {
int type = iterator.currentSegment(coords);
double x = coords[0];
double y = coords[1];
switch (type) {
case PathIterator.SEG_MOVETO:
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
break;
case PathIterator.SEG_LINETO:
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
break;
case PathIterator.SEG_QUADTO:
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
x = coords[2];
y = coords[3];
graphics.drawRect((int) (x - markerSize / 2), (int) (y - markerSize / 2), markerSize, markerSize);
break;
case PathIterator.SEG_CUBICTO:
System.out.print("CUBICTO NOT SUPPORTED. ");
break;
case PathIterator.SEG_CLOSE:
System.out.print("CLOSE NOT SUPPORTED. ");
break;
}
iterator.next();
}
}
}
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShapes(), getCharacterId(), exporter, null, colorTransform, 1);
shapeExporter.export();
}
@Override
public void toHtmlCanvas(StringBuilder result, double unitDivisor) {
CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, getShapes(), null, 0, 0);
cse.export();
result.append(cse.getShapeData());
}
@Override
public int getNumFrames() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public int getCharacterId() {
return shapeId;
}
@Override
public void setCharacterId(int characterId) {
this.shapeId = characterId;
}
}