Render artwork at correct resolution when using vello on wasm (#3416)

* Work on fixing rendering for wasm+vello

* Render vello canvas in wasm at the correct resolution

* Cleanup unused surface rendering code

* Remove vector to raster conversion

* Remove desktop changes

* Revert window.rs changes

* Don't round logical coordinates

* Fix desktop compilation + don't round logical coordinates for svg rendering

* Further cleanup

* Compute logical size from acutal physical sizes
This commit is contained in:
Dennis Kobert
2025-11-24 15:23:27 +01:00
committed by GitHub
parent 6e66c79392
commit a932eaedcf
13 changed files with 197 additions and 216 deletions

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onMount, tick } from "svelte";
import { getContext, onMount, onDestroy, tick } from "svelte";
import type { Editor } from "@graphite/editor";
import {
@@ -20,7 +20,7 @@
import type { DocumentState } from "@graphite/state-providers/document";
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
import { updateBoundsOfViewports as updateViewport } from "@graphite/utility-functions/viewports";
import { setupViewportResizeObserver, cleanupViewportResizeObserver } from "@graphite/utility-functions/viewports";
import EyedropperPreview, { ZOOM_WINDOW_DIMENSIONS } from "@graphite/components/floating-menus/EyedropperPreview.svelte";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
@@ -203,9 +203,18 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let canvas = (window as any).imageCanvases[canvasName];
if (canvasName !== "0" && canvas.parentElement) {
var newCanvas = window.document.createElement("canvas");
var context = newCanvas.getContext("2d");
// Get logical dimensions from foreignObject parent (set by backend)
const foreignObject = placeholder.parentElement;
if (!foreignObject) return;
const logicalWidth = parseFloat(foreignObject.getAttribute("width") || "0");
const logicalHeight = parseFloat(foreignObject.getAttribute("height") || "0");
// Clone canvas for repeated instances (layers that appear multiple times)
// Viewport canvas is marked with data-is-viewport and should never be cloned
const isViewport = placeholder.hasAttribute("data-is-viewport");
if (!isViewport && canvas.parentElement) {
const newCanvas = window.document.createElement("canvas");
const context = newCanvas.getContext("2d");
newCanvas.width = canvas.width;
newCanvas.height = canvas.height;
@@ -215,6 +224,10 @@
canvas = newCanvas;
}
// Set CSS size to logical resolution (for correct display size)
canvas.style.width = `${logicalWidth}px`;
canvas.style.height = `${logicalHeight}px`;
placeholder.replaceWith(canvas);
});
}
@@ -393,8 +406,8 @@
rulerHorizontal?.resize();
rulerVertical?.resize();
// Send the new bounds of the viewports to the backend
if (viewport.parentElement) updateViewport(editor);
// Note: Viewport bounds are now sent to the backend by the ResizeObserver in viewports.ts
// which provides pixel-perfect physical dimensions via devicePixelContentBoxSize
}
onMount(() => {
@@ -473,14 +486,21 @@
displayRemoveEditableTextbox();
});
// Once this component is mounted, we want to resend the document bounds to the backend via the resize event handler which does that
window.dispatchEvent(new Event("resize"));
// Setup ResizeObserver for pixel-perfect viewport tracking with physical dimensions
// This must happen in onMount to ensure the viewport container element exists
setupViewportResizeObserver(editor);
// Also observe the inner viewport for canvas sizing and ruler updates
const viewportResizeObserver = new ResizeObserver(() => {
updateViewportInfo();
});
if (viewport) viewportResizeObserver.observe(viewport);
});
onDestroy(() => {
// Cleanup the viewport resize observer
cleanupViewportResizeObserver();
});
</script>
<LayoutCol class="document" on:dragover={(e) => e.preventDefault()} on:drop={dropFile}>