From 004e2ecdfd9dd471e526edb7e2f3ac3b4ea70d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 6 Mar 2021 22:56:05 +0100 Subject: [PATCH] #1156 SVG Export - 9 slice scaling (DefineScalingGrid) --- CHANGELOG.md | 2 +- .../exporters/commonshape/SVGExporter.java | 189 +++++++++++++++++- .../decompiler/flash/tags/base/TextTag.java | 5 +- .../decompiler/flash/timeline/Timeline.java | 8 +- 4 files changed, 195 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccf54090a..34814b785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. - [#1645] Scrollbar to recent searches dropdown - [#1639] Clearing search results for current file - [#1371] Go to character id (Ctrl+G in tag tree) -- [#1156] FLA Export - 9 slice scaling (DefineScalingGrid) +- [#1156] FLA Export, SVG Export - 9 slice scaling (DefineScalingGrid) ### Changed - [#1471] Import script menuitem renamed to Import scripts. diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java index 4e79452f8..991845f4e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/SVGExporter.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.exporters.commonshape; import com.jpexs.decompiler.flash.SWF; @@ -149,7 +150,7 @@ public class SVGExporter { g.getParentNode().removeChild(g); return false; } - + return true; } @@ -215,7 +216,189 @@ public class SVGExporter { attr.setValue("background: " + new RGBA(backGroundColor).toHexARGB()); } - public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName) { + private void addScalingGridUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect) { + ExportRectangle exRect = new ExportRectangle(boundRect); + ExportRectangle newRect = exRect; + if (transform == null) { + transform = new Matrix(); + } + Matrix transform2; + newRect = transform.transform(exRect); + transform = transform.clone(); + + transform = Matrix.getTranslateInstance(newRect.xMin, newRect.yMin); + + double scaleWidth = newRect.getWidth() - scalingRect.Xmin - (boundRect.Xmax - scalingRect.Xmax); + double originalWidth = boundRect.getWidth() - scalingRect.Xmin - (boundRect.Xmax - scalingRect.Xmax); + double scaleX = scaleWidth / originalWidth; + + double scaleHeight = newRect.getHeight() - scalingRect.Ymin - (boundRect.Ymax - scalingRect.Ymax); + double originalHeight = boundRect.getHeight() - scalingRect.Ymin - (boundRect.Ymax - scalingRect.Ymax); + double scaleY = scaleHeight / originalHeight; + + Element leftTopCorner = _svg.createElement("use"); + leftTopCorner.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1)); + + leftTopCorner.setAttribute("clip-path", "path('" + + "M 0,0 " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + ",0 " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L 0," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " Z" + + "')"); + leftTopCorner.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(leftTopCorner); + + Element bottomLeftCorner = _svg.createElement("use"); + transform2 = transform.clone(); + transform2.translateY += newRect.getHeight() - boundRect.getHeight(); + bottomLeftCorner.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + bottomLeftCorner.setAttribute("clip-path", "path('" + + "M 0," + Math.rint((boundRect.getHeight() - (boundRect.Ymax - scalingRect.Ymax)) / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint((boundRect.getHeight() - (boundRect.Ymax - scalingRect.Ymax)) / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(boundRect.getHeight() / (double) SWF.unitDivisor) + " " + + "L 0," + Math.rint(boundRect.getHeight() / (double) SWF.unitDivisor) + " Z" + + "')"); + + bottomLeftCorner.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(bottomLeftCorner); + + Element topRightCorner = _svg.createElement("use"); + transform2 = transform.clone(); + transform2.translateX += newRect.getWidth() - boundRect.getWidth(); + topRightCorner.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + topRightCorner.setAttribute("clip-path", "path('" + + "M " + Math.rint((boundRect.getWidth() - (exRect.xMax - scalingRect.Xmax)) / (double) SWF.unitDivisor) + ",0 " + + "L " + Math.rint(boundRect.getWidth() / (double) SWF.unitDivisor) + ",0 " + + "L " + Math.rint(boundRect.getWidth() / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint((boundRect.getWidth() - (exRect.xMax - scalingRect.Xmax)) / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " Z" + + "')"); + topRightCorner.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(topRightCorner); + + Element bottomRightCorner = _svg.createElement("use"); + transform2 = transform.clone(); + transform2.translateX += newRect.getWidth() - boundRect.getWidth(); + transform2.translateY += newRect.getHeight() - boundRect.getHeight(); + bottomRightCorner.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + bottomRightCorner.setAttribute("clip-path", "path('" + + "M " + Math.rint((boundRect.getWidth() - (exRect.xMax - scalingRect.Xmax)) / (double) SWF.unitDivisor) + "," + Math.rint((boundRect.getHeight() - (boundRect.Ymax - scalingRect.Ymax)) / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(boundRect.getWidth() / (double) SWF.unitDivisor) + "," + Math.rint((boundRect.getHeight() - (boundRect.Ymax - scalingRect.Ymax)) / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(boundRect.getWidth() / (double) SWF.unitDivisor) + "," + Math.rint(boundRect.getHeight() / (double) SWF.unitDivisor) + " " + + "L " + Math.rint((boundRect.getWidth() - (exRect.xMax - scalingRect.Xmax)) / (double) SWF.unitDivisor) + "," + Math.rint(boundRect.getHeight() / (double) SWF.unitDivisor) + " Z" + + "')"); + bottomRightCorner.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(bottomRightCorner); + + Element top = _svg.createElement("use"); + transform2 = transform.clone(); + + transform2.translate(scalingRect.Xmin, 0); + transform2.scale(scaleX, 1); + transform2.translate(-scalingRect.Xmin, 0); + + top.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + top.setAttribute("clip-path", "path('" + + "M " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + ",0 " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + ",0 " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " Z" + + "')"); + + top.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(top); + + Element left = _svg.createElement("use"); + transform2 = transform.clone(); + + transform2.translate(0, scalingRect.Ymin); + transform2.scale(1, scaleY); + transform2.translate(0, -scalingRect.Ymin); + + left.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + left.setAttribute("clip-path", "path('" + + "M 0," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " " + + "L 0," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " Z" + + "')"); + + left.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(left); + + Element bottom = _svg.createElement("use"); + transform2 = transform.clone(); + + transform2.translate(scalingRect.Xmin, 0); + transform2.scale(scaleX, 1); + transform2.translate(-scalingRect.Xmin, 0); + + transform2.translateY += newRect.getHeight() - boundRect.getHeight(); + + bottom.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + bottom.setAttribute("clip-path", "path('" + + "M " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(boundRect.Ymax / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(boundRect.Ymax / (double) SWF.unitDivisor) + " Z" + + "')"); + + bottom.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(bottom); + + Element right = _svg.createElement("use"); + transform2 = transform.clone(); + + transform2.translate(0, scalingRect.Ymin); + transform2.scale(1, scaleY); + transform2.translate(0, -scalingRect.Ymin); + + transform2.translateX += newRect.getWidth() - boundRect.getWidth(); + + right.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + right.setAttribute("clip-path", "path('" + + "M " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(boundRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(boundRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " Z" + + "')"); + + right.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(right); + + Element center = _svg.createElement("use"); + + transform2 = transform.clone(); + + transform2.translate(scalingRect.Xmin, scalingRect.Ymin); + transform2.scale(scaleX, scaleY); + transform2.translate(-scalingRect.Xmin, -scalingRect.Ymin); + + center.setAttribute("transform", transform2.getSvgTransformationString(SWF.unitDivisor, 1)); + + center.setAttribute("clip-path", "path('" + + "M " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymin / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmax / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " " + + "L " + Math.rint(scalingRect.Xmin / (double) SWF.unitDivisor) + "," + Math.rint(scalingRect.Ymax / (double) SWF.unitDivisor) + " Z" + + "')"); + + center.setAttribute("xlink:href", "#" + href); + _svgGs.peek().appendChild(center); + + //TODO: how to include instance name + } + + public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect) { + if (scalingRect != null && (transform == null || (Double.compare(transform.rotateSkew0, 0.0) == 0 && Double.compare(transform.rotateSkew1, 0.0) == 0))) { + addScalingGridUse(transform, boundRect, href, instanceName, scalingRect); + return null; //?? + } Element image = _svg.createElement("use"); if (transform != null) { image.setAttribute("transform", transform.getSvgTransformationString(SWF.unitDivisor, 1)); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index c410a0b7c..4ddaa0610 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.SWF; @@ -759,7 +760,7 @@ public abstract class TextTag extends DrawableTag { } if (!"".equals(charId)) { - Element charImage = exporter.addUse(mat, bounds, charId, null); + Element charImage = exporter.addUse(mat, bounds, charId, null, null); RGBA colorA = new RGBA(textColor); charImage.setAttribute("fill", colorA.toHexRGB()); if (colorA.alpha != 255) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 1606f6c34..e51ed6ae4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -1055,19 +1055,21 @@ public class Timeline { } ExportRectangle rect = new ExportRectangle(boundRect); + DefineScalingGridTag scalingGrid = character.getScalingGridTag(); + // TODO: if (layer.filters != null) - // TODO: if (layer.blendMode > 1) + // TODO: if (layer.blendMode > 1) if (layer.clipDepth > -1) { String clipName = exporter.getUniqueId("clipPath"); exporter.createClipPath(new Matrix(), clipName); SvgClip clip = new SvgClip(clipName, layer.clipDepth); clips.add(clip); Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName, layer.instanceName); + exporter.addUse(mat, boundRect, assetName, layer.instanceName, scalingGrid == null ? null : scalingGrid.splitter); exporter.endGroup(); } else { Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName, layer.instanceName); + exporter.addUse(mat, boundRect, assetName, layer.instanceName, scalingGrid == null ? null : scalingGrid.splitter); } } }