diff options
| author | capellancitizen <thecapellancitizen@gmail.com> | 2024-05-01 14:05:24 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-01 14:05:24 -0400 |
| commit | 8d36d2aaa0a48e065a25a16f0dad1d24bb8612f0 (patch) | |
| tree | 5d6c47eeeea90f345d91ced6e97e9162ac6b804e | |
| parent | 298f911ec588b99786e6adcc4ae5015c1717fc3d (diff) | |
Improved stitch plan rasterization performance (#2873)
| -rw-r--r-- | lib/extensions/stitch_plan_preview.py | 49 |
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 |
