mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-31 04:35:39 +00:00
feat: combined button SVG export using all states
This commit is contained in:
@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings;
|
||||
@@ -40,6 +41,7 @@ import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.FontTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.RenderContext;
|
||||
import com.jpexs.decompiler.flash.tags.base.TextTag;
|
||||
@@ -107,6 +109,7 @@ import net.kroo.elliot.GifSequenceWriter;
|
||||
import net.weiner.kevin.AnimatedGifEncoder;
|
||||
import org.monte.media.VideoFormatKeys;
|
||||
import org.monte.media.avi.AVIWriter;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Frame exporter.
|
||||
@@ -129,6 +132,9 @@ public class FrameExporter {
|
||||
case SVG:
|
||||
fem = FrameExportMode.SVG;
|
||||
break;
|
||||
case SVG_COMBINED:
|
||||
fem = FrameExportMode.SVG;
|
||||
break;
|
||||
case WEBP:
|
||||
fem = FrameExportMode.WEBP;
|
||||
break;
|
||||
@@ -139,16 +145,16 @@ public class FrameExporter {
|
||||
throw new Error("Unsupported button export mode: " + settings.mode);
|
||||
}
|
||||
|
||||
if (frames == null) {
|
||||
/*if (frames == null) {
|
||||
frames = new ArrayList<>();
|
||||
frames.add(ButtonTag.FRAME_UP);
|
||||
frames.add(ButtonTag.FRAME_OVER);
|
||||
frames.add(ButtonTag.FRAME_DOWN);
|
||||
frames.add(ButtonTag.FRAME_HITTEST);
|
||||
}
|
||||
}*/
|
||||
|
||||
FrameExportSettings fes = new FrameExportSettings(fem, settings.zoom, true, settings.aaScale);
|
||||
return exportFrames(handler, outdir, swf, containerId, frames, 1, fes, evl, true);
|
||||
return exportFrames(handler, outdir, swf, containerId, frames, 1, fes, evl, true, settings.mode == ButtonExportMode.SVG_COMBINED);
|
||||
}
|
||||
|
||||
public List<File> exportSpriteFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List<Integer> frames, int subframesLength, SpriteExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
@@ -275,10 +281,6 @@ public class FrameExporter {
|
||||
}
|
||||
}
|
||||
|
||||
public List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List<Integer> frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl) throws IOException, InterruptedException {
|
||||
return exportFrames(handler, outdir, swf, containerId, frames, subFramesLength, settings, evl, false);
|
||||
}
|
||||
|
||||
private String getFrameFileName(int frame, boolean button) {
|
||||
String buttonSuffix = "";
|
||||
if (button) {
|
||||
@@ -300,7 +302,11 @@ public class FrameExporter {
|
||||
return "" + frame + buttonSuffix;
|
||||
}
|
||||
|
||||
public List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List<Integer> frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl, boolean button) throws IOException, InterruptedException {
|
||||
public List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List<Integer> frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl) throws IOException, InterruptedException {
|
||||
return exportFrames(handler, outdir, swf, containerId, frames, subFramesLength, settings, evl, false, false);
|
||||
}
|
||||
|
||||
private List<File> exportFrames(AbortRetryIgnoreHandler handler, String outdir, final SWF swf, int containerId, List<Integer> frames, int subFramesLength, final FrameExportSettings settings, final EventListener evl, boolean button, boolean combined) throws IOException, InterruptedException {
|
||||
final List<File> ret = new ArrayList<>();
|
||||
if (CancellableWorker.isInterrupted()) {
|
||||
return ret;
|
||||
@@ -309,6 +315,7 @@ public class FrameExporter {
|
||||
if (swf.getTags().isEmpty()) {
|
||||
return ret;
|
||||
}
|
||||
Timelined timelined;
|
||||
Timeline tim0;
|
||||
List<String> paths = new ArrayList<>();
|
||||
String subPath = "";
|
||||
@@ -317,10 +324,12 @@ public class FrameExporter {
|
||||
}
|
||||
|
||||
if (containerId == 0) {
|
||||
timelined = swf;
|
||||
tim0 = swf.getTimeline();
|
||||
paths.add(subPath);
|
||||
} else {
|
||||
tim0 = ((Timelined) swf.getCharacter(containerId)).getTimeline();
|
||||
timelined = (Timelined) swf.getCharacter(containerId);
|
||||
tim0 = timelined.getTimeline();
|
||||
|
||||
Set<String> classNames = swf.getCharacter(containerId).getClassNames();
|
||||
if (Configuration.as3ExportNamesUseClassNamesOnly.get() && !classNames.isEmpty()) {
|
||||
@@ -367,23 +376,12 @@ public class FrameExporter {
|
||||
Map<Integer, FontTag> normalizedFonts = new LinkedHashMap<>();
|
||||
Map<Integer, TextTag> normalizedTexts = new LinkedHashMap<>();
|
||||
normalizer.normalizeFonts(tim.timelined.getSwf(), normalizedFonts, normalizedTexts);
|
||||
|
||||
int max = frames.size();
|
||||
if (subFramesLength > 1) {
|
||||
max = subFramesLength;
|
||||
}
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (evl != null) {
|
||||
Tag parentTag = tim.getParentTag();
|
||||
evl.handleExportingEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName());
|
||||
}
|
||||
|
||||
final int fi = i;
|
||||
|
||||
if (combined) {
|
||||
final Color fbackgroundColor = backgroundColor;
|
||||
for (File foutdir : foutdirs) {
|
||||
new RetryTask(() -> {
|
||||
int frame = subFramesLength > 1 ? fframes.get(0) : fframes.get(fi);
|
||||
File f = new File(foutdir + File.separator + getFrameFileName(((subFramesLength > 1 ? fi : fframes.get(fi)) + 1), button) + ".svg");
|
||||
File f = new File(foutdir + File.separator + "combined.svg");
|
||||
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) {
|
||||
ExportRectangle rect = new ExportRectangle(tim.displayRect);
|
||||
rect.xMax *= settings.zoom;
|
||||
@@ -391,21 +389,56 @@ public class FrameExporter {
|
||||
rect.xMin *= settings.zoom;
|
||||
rect.yMin *= settings.zoom;
|
||||
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "frame", fbackgroundColor);
|
||||
exporter.setNormalizedFonts(normalizedFonts, normalizedTexts);
|
||||
tim.toSVG(frame, subFramesLength > 1 ? fi : 0, null, 0, exporter, null, 0, new Matrix(), new Matrix());
|
||||
exporter.setNormalizedFonts(normalizedFonts, normalizedTexts);
|
||||
//Here is not timelined.toSVG, but drawableTag.toSVG, which draws combined button
|
||||
((DrawableTag) timelined).toSVG(0, 0, exporter, 0, null, 0, new Matrix(), new Matrix());
|
||||
|
||||
|
||||
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
|
||||
}
|
||||
ret.add(f);
|
||||
}, handler).run();
|
||||
}
|
||||
|
||||
if (CancellableWorker.isInterrupted()) {
|
||||
break;
|
||||
} else {
|
||||
int max = frames.size();
|
||||
if (subFramesLength > 1) {
|
||||
max = subFramesLength;
|
||||
}
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (evl != null) {
|
||||
Tag parentTag = tim.getParentTag();
|
||||
evl.handleExportingEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName());
|
||||
}
|
||||
|
||||
if (evl != null) {
|
||||
Tag parentTag = tim.getParentTag();
|
||||
evl.handleExportedEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName());
|
||||
final int fi = i;
|
||||
final Color fbackgroundColor = backgroundColor;
|
||||
for (File foutdir : foutdirs) {
|
||||
new RetryTask(() -> {
|
||||
int frame = subFramesLength > 1 ? fframes.get(0) : fframes.get(fi);
|
||||
File f = new File(foutdir + File.separator + getFrameFileName(((subFramesLength > 1 ? fi : fframes.get(fi)) + 1), button) + ".svg");
|
||||
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) {
|
||||
ExportRectangle rect = new ExportRectangle(tim.displayRect);
|
||||
rect.xMax *= settings.zoom;
|
||||
rect.yMax *= settings.zoom;
|
||||
rect.xMin *= settings.zoom;
|
||||
rect.yMin *= settings.zoom;
|
||||
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "frame", fbackgroundColor);
|
||||
exporter.setNormalizedFonts(normalizedFonts, normalizedTexts);
|
||||
tim.toSVG(frame, subFramesLength > 1 ? fi : 0, null, 0, exporter, null, 0, new Matrix(), new Matrix());
|
||||
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
|
||||
}
|
||||
ret.add(f);
|
||||
}, handler).run();
|
||||
}
|
||||
|
||||
if (CancellableWorker.isInterrupted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (evl != null) {
|
||||
Tag parentTag = tim.getParentTag();
|
||||
evl.handleExportedEvent("frame", i + 1, max, parentTag == null ? "" : parentTag.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,8 @@ public class SVGExporter implements RequiresNormalizedFonts {
|
||||
|
||||
private Map<Integer, FontTag> normalizedFonts = new LinkedHashMap<>();
|
||||
private Map<Integer, TextTag> normalizedTexts = new LinkedHashMap<>();
|
||||
|
||||
private boolean buttonStyleAdded = false;
|
||||
|
||||
@Override
|
||||
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts) {
|
||||
@@ -656,7 +658,55 @@ public class SVGExporter implements RequiresNormalizedFonts {
|
||||
}
|
||||
}
|
||||
|
||||
public void addStyle(String fontFace, byte[] data, FontExportMode mode) {
|
||||
public void addStyle(String css) {
|
||||
String value = getStyle().getTextContent();
|
||||
value += Helper.newLine;
|
||||
value += css;
|
||||
getStyle().setTextContent(value);
|
||||
}
|
||||
|
||||
public void requireButtonStyle() {
|
||||
if (buttonStyleAdded) {
|
||||
return;
|
||||
}
|
||||
buttonStyleAdded = true;
|
||||
addStyle(" .button-frame {\n"
|
||||
+ " pointer-events: none;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " .button-frame-up,\n"
|
||||
+ " .button-frame-over,\n"
|
||||
+ " .button-frame-down {\n"
|
||||
+ " opacity: 0;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " .button .button-frame-up {\n"
|
||||
+ " opacity: 1;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " .button:hover .button-frame-up {\n"
|
||||
+ " opacity: 0;\n"
|
||||
+ " }\n"
|
||||
+ " .button:hover .button-frame-over {\n"
|
||||
+ " opacity: 1;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " .button:active .button-frame-over {\n"
|
||||
+ " opacity: 0;\n"
|
||||
+ " }\n"
|
||||
+ " .button:active .button-frame-down {\n"
|
||||
+ " opacity: 1;\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " .button-frame-hittest {\n"
|
||||
+ " opacity: 0;\n"
|
||||
+ " pointer-events: all;\n"
|
||||
+ " cursor: pointer;\n"
|
||||
+ " }\n");
|
||||
|
||||
}
|
||||
|
||||
public void addFontFace(String fontFace, byte[] data, FontExportMode mode) {
|
||||
if (!fontFaces.contains(fontFace)) {
|
||||
fontFaces.add(fontFace);
|
||||
String base64Data = Helper.byteArrayToBase64String(data);
|
||||
|
||||
@@ -32,6 +32,10 @@ public enum ButtonExportMode {
|
||||
* SVG - Scalable Vector Graphics
|
||||
*/
|
||||
SVG,
|
||||
/**
|
||||
* SVG - Scalable Vector Graphics - Combined button
|
||||
*/
|
||||
SVG_COMBINED,
|
||||
/**
|
||||
* BMP - Windows Bitmap
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Base class for button tags.
|
||||
@@ -134,7 +135,36 @@ public abstract class ButtonTag extends DrawableTag implements Timelined {
|
||||
|
||||
@Override
|
||||
public void toSVG(int frame, int time, SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
|
||||
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
|
||||
//getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
|
||||
exporter.requireButtonStyle();
|
||||
|
||||
Timeline tim = getTimeline();
|
||||
|
||||
Element buttonGroup = exporter.createSubGroup(new Matrix(), null);
|
||||
buttonGroup.setAttribute("class", "button");
|
||||
|
||||
Element upGroup = exporter.createSubGroup(new Matrix(), null);
|
||||
upGroup.setAttribute("class", "button-frame button-frame-up");
|
||||
tim.toSVG(ButtonTag.FRAME_UP, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
|
||||
exporter.endGroup();
|
||||
|
||||
Element overGroup = exporter.createSubGroup(new Matrix(), null);
|
||||
overGroup.setAttribute("class", "button-frame button-frame-over");
|
||||
tim.toSVG(ButtonTag.FRAME_OVER, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
|
||||
exporter.endGroup();
|
||||
|
||||
Element downGroup = exporter.createSubGroup(new Matrix(), null);
|
||||
downGroup.setAttribute("class", "button-frame button-frame-down");
|
||||
tim.toSVG(ButtonTag.FRAME_DOWN, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
|
||||
exporter.endGroup();
|
||||
|
||||
Element hitTestGroup = exporter.createSubGroup(new Matrix(), null);
|
||||
hitTestGroup.setAttribute("class", "button-frame button-frame-hittest");
|
||||
tim.toSVG(ButtonTag.FRAME_HITTEST, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
|
||||
exporter.endGroup();
|
||||
|
||||
exporter.endGroup(); //buttonGroup
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1114,7 +1114,7 @@ public abstract class TextTag extends DrawableTag {
|
||||
|
||||
exporter.addToGroup(textElement);
|
||||
FontExportMode fontExportMode = FontExportMode.WOFF;
|
||||
exporter.addStyle(fontFamily, new FontExporter().exportFont(font, fontExportMode), fontExportMode);
|
||||
exporter.addFontFace(fontFamily, new FontExporter().exportFont(font, fontExportMode), fontExportMode);
|
||||
|
||||
if (hasOffset) {
|
||||
exporter.endGroup();
|
||||
|
||||
Reference in New Issue
Block a user