summaryrefslogtreecommitdiff
path: root/lib/extensions/stitch_plan_preview.py
diff options
context:
space:
mode:
authorcapellancitizen <thecapellancitizen@gmail.com>2024-05-01 14:05:24 -0400
committerGitHub <noreply@github.com>2024-05-01 14:05:24 -0400
commit8d36d2aaa0a48e065a25a16f0dad1d24bb8612f0 (patch)
tree5d6c47eeeea90f345d91ced6e97e9162ac6b804e /lib/extensions/stitch_plan_preview.py
parent298f911ec588b99786e6adcc4ae5015c1717fc3d (diff)
Improved stitch plan rasterization performance (#2873)
Diffstat (limited to 'lib/extensions/stitch_plan_preview.py')
-rw-r--r--lib/extensions/stitch_plan_preview.py49
1 files changed, 38 insertions, 11 deletions
diff --git a/lib/extensions/stitch_plan_preview.py b/lib/extensions/stitch_plan_preview.py
index f8dfc60d..0a7a4f28 100644
--- a/lib/extensions/stitch_plan_preview.py
+++ b/lib/extensions/stitch_plan_preview.py
@@ -8,8 +8,8 @@ from base64 import b64encode
from typing import Optional, Tuple
import sys
-from inkex import errormsg, Boolean, BoundingBox, Image, BaseElement
-from inkex.command import take_snapshot
+from inkex import errormsg, Boolean, Image, BaseElement
+from inkex.command import inkscape
from ..marker import set_marker
from ..stitch_plan import stitch_groups_to_stitch_plan
@@ -96,22 +96,49 @@ class StitchPlanPreview(InkstitchExtension):
if layer is not None:
layer.set('id', svg.get_unique_id('inkstitch_stitch_plan_'))
- def rasterize(self, svg, layer: BaseElement, raster_mult: Optional[int]) -> BaseElement:
+ def rasterize(self, svg: BaseElement, layer: BaseElement, raster_mult: Optional[int]) -> BaseElement:
if raster_mult is None:
# Don't rasterize if there's no reason to.
return layer
else:
with TemporaryDirectory() as tempdir:
- bbox: BoundingBox = layer.bounding_box()
- rasterized_file = take_snapshot(svg, tempdir, dpi=96*raster_mult,
- export_id=layer.get_id(), export_id_only=True)
- with open(rasterized_file, "rb") as f:
+ # Inkex's command functionality also writes files to temp directories like this.
+ temp_svg_path = f"{tempdir}/temp.svg"
+ temp_png_path = f"{tempdir}/temp.png"
+ with open(temp_svg_path, "wb") as f:
+ f.write(svg.tostring())
+
+ # We need the bounding box of the stitch layer so we can place the rasterized version in the same place.
+ # however, layer.bounding_box() is pure python, so it can be very slow for more complex stitch previews.
+ # Instead, especially because we need to invoke Inkscape anyway to perform the rasterization, we get
+ # the bounding box with query commands before we perform the export. This is quite cheap.
+ out = inkscape(temp_svg_path, actions="; ".join([
+ f"select-by-id:{layer.get_id()}",
+ "query-x",
+ "query-y",
+ "query-width",
+ "query-height",
+ f"export-id:{layer.get_id()}",
+ "export-id-only",
+ "export-type:png",
+ f"export-dpi:{96*raster_mult}",
+ f"export-filename:{temp_png_path}",
+ "export-do" # Inkscape docs say this should be implicit at the end, but it doesn't seem to be.
+ ]))
+
+ # The query commands return positions in px, so we need to convert to uu.
+ px_to_uu = svg.unittouu("1px")
+ # Parse the returned coordinates out.
+ x, y, width, height = map(lambda x: px_to_uu*float(x), out.split())
+
+ # Embed the rasterized stitch plan into the SVG, and replace the original stitch plan
+ with open(temp_png_path, "rb") as f:
image = Image(attrib={
XLINK_HREF: f"data:image/png;base64,{b64encode(f.read()).decode()}",
- "x": str(bbox.left),
- "y": str(bbox.top),
- "height": str(bbox.height),
- "width": str(bbox.width),
+ "x": str(x),
+ "y": str(y),
+ "height": str(height),
+ "width": str(width),
})
layer.replace_with(image)
return image