From 4595a39a9106d07a1955cddf518234f86f8e65b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Thu, 8 May 2025 11:07:07 +0200 Subject: [PATCH] Added: #2453 SVG export/import - use image-rendering attribute for image smoothing --- CHANGELOG.md | 2 + .../exporters/shape/SVGShapeExporter.java | 9 ++- .../importers/svg/SvgImageRendering.java | 39 +++++++++++++ .../flash/importers/svg/SvgStyle.java | 55 +++++++++++++++++-- .../flash/importers/svg/SvgStyleProperty.java | 1 + 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImageRendering.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 40c0b374c..6f24f9f27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file. - [#2370] Snap align border space, object spacing, center alignment - [#2370] Setting for color and snap accuracy for guides, grid - [#2370] Dialogs for editing grid, guides and snapping +- [#2453] SVG export/import - use image-rendering attribute for image smoothing ### Fixed - [#2424] DefineEditText handling of letterSpacing, font size on incorrect values @@ -3753,6 +3754,7 @@ Major version of SWF to XML export changed to 2. [#1826]: https://www.free-decompiler.com/flash/issues/1826 [#2448]: https://www.free-decompiler.com/flash/issues/2448 [#2370]: https://www.free-decompiler.com/flash/issues/2370 +[#2453]: https://www.free-decompiler.com/flash/issues/2453 [#2424]: https://www.free-decompiler.com/flash/issues/2424 [#2391]: https://www.free-decompiler.com/flash/issues/2391 [#2436]: https://www.free-decompiler.com/flash/issues/2436 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java index f7ec291ba..ba6af5947 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/SVGShapeExporter.java @@ -162,7 +162,8 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter { pattern.setAttribute("width", "" + width); pattern.setAttribute("height", "" + height); pattern.setAttribute("viewBox", "0 0 " + width + " " + height); - pattern.setAttribute("ffdec:smoothed", smoothed ? "true" : "false"); + //Smoothed attribute was used in older FFDec versions - it is now only used for reading, for backwards compatibility + //pattern.setAttribute("ffdec:smoothed", smoothed ? "true" : "false"); if (matrix != null) { pattern.setAttribute("patternTransform", matrix.getSvgTransformationString(SWF.unitDivisor / zoom, SWF.unitDivisor / zoom)); } @@ -170,6 +171,12 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter { imageElement.setAttribute("width", "" + width); imageElement.setAttribute("height", "" + height); imageElement.setAttribute("xlink:href", "data:image/" + format + ";base64," + base64ImgData); + if (smoothed) { + imageElement.setAttribute("style", "image-rendering:optimizeQuality"); + } else { + //https://stackoverflow.com/questions/50184674/stop-auto-image-smoothing-inside-an-svgz + imageElement.setAttribute("style", "image-rendering:optimizeSpeed; image-rendering:pixelated"); + } pattern.appendChild(imageElement); exporter.addToGroup(pattern); return patternId; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImageRendering.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImageRendering.java new file mode 100644 index 000000000..abe2347b7 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgImageRendering.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010-2024 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.importers.svg; + +/** + * + * @author JPEXS + */ +public enum SvgImageRendering { + + /** + * Optimize speed aka Pixelated + */ + OPTIMIZE_SPEED, + + /** + * Optimize quality + */ + OPTIMIZE_QUALITY, + + /** + * Automatic + */ + AUTO +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java index 577d02d2e..7b9e619a6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyle.java @@ -657,7 +657,31 @@ class SvgStyle { if (e.hasAttribute("patternTransform")) { bitmapFill.patternTransform = e.getAttribute("patternTransform"); } - if (e.hasAttribute("ffdec:smoothed")) { + + NodeList childNodes = e.getChildNodes(); + Element element = null; + for (int i = 0; i < childNodes.getLength(); i++) { + if (childNodes.item(i) instanceof Element) { + + if ("animateTransform".equals(((Element) childNodes.item(i)).getTagName())) { + continue; + } + + if (element != null) { + element = null; + break; + } + + element = (Element) childNodes.item(i); + } + } + + SvgImageRendering imageRendering = null; + if (element != null) { + imageRendering = getValue(element, "image-rendering", true); + } + + if (e.hasAttribute("ffdec:smoothed")) { //backwards compatibility String smoothedValue = e.getAttribute("ffdec:smoothed").trim(); if ("true".equals(smoothedValue)) { bitmapFill.smoothed = true; @@ -665,8 +689,12 @@ class SvgStyle { if ("false".equals(smoothedValue)) { bitmapFill.smoothed = false; } - } else { - bitmapFill.smoothed = true; + } else { + if (imageRendering == SvgImageRendering.OPTIMIZE_SPEED) { + bitmapFill.smoothed = false; + } else { + bitmapFill.smoothed = true; + } } return bitmapFill; } @@ -714,7 +742,8 @@ class SvgStyle { if (e.hasAttribute("patternTransform")) { bitmapFill.patternTransform = e.getAttribute("patternTransform"); } - if (e.hasAttribute("ffdec:smoothed")) { + SvgImageRendering imageRendering = getValue(element, "image-rendering", true); + if (e.hasAttribute("ffdec:smoothed")) { //backwards compatibility String smoothedValue = e.getAttribute("ffdec:smoothed").trim(); if ("true".equals(smoothedValue)) { bitmapFill.smoothed = true; @@ -723,7 +752,11 @@ class SvgStyle { bitmapFill.smoothed = false; } } else { - bitmapFill.smoothed = true; + if (imageRendering == SvgImageRendering.OPTIMIZE_SPEED) { + bitmapFill.smoothed = false; + } else { + bitmapFill.smoothed = true; + } } cachedBitmaps.put(elementId, imageTag.characterID); @@ -828,6 +861,18 @@ class SvgStyle { } } break; + case "image-rendering": { + switch (value) { + case "optimizeSpeed": + case "pixelated": //a weird value used by the Chrome browser + return SvgImageRendering.OPTIMIZE_SPEED; + case "optimizeQuality": + return SvgImageRendering.OPTIMIZE_QUALITY; + case "auto": + return SvgImageRendering.AUTO; + } + } + break; case "stroke-miterlimit": { double strokeMiterLimit = Double.parseDouble(value); return strokeMiterLimit; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java index b2cc536ef..e4876cfa1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/svg/SvgStyleProperty.java @@ -88,6 +88,7 @@ public class SvgStyleProperty { p.put("stop-color", new SvgStyleProperty("stop-color", false, Color.BLACK)); p.put("stop-opacity", new SvgStyleProperty("stop-opacity", false, 1.0)); p.put("vector-effect", new SvgStyleProperty("vector-effect", false, "none")); + p.put("image-rendering", new SvgStyleProperty("image-rendering", true, SvgImageRendering.AUTO)); properties = p; }