Capture snapshot only when needed and downscaled

This commit is contained in:
Elena Torro 2026-06-11 12:23:51 +02:00
parent 12e7ea038f
commit 1ba43c863a
2 changed files with 37 additions and 43 deletions

View File

@ -86,10 +86,6 @@
(defonce transition-image-url* (atom nil))
(defonce transition-epoch* (atom 0))
(defonce transition-tiles-handler* (atom nil))
(defonce snapshot-tiles-handler* (atom nil))
(def ^:private snapshot-capture-debounce-ms 250)
(defn initialized?
"True when the WASM render context is ready to receive design-state
@ -173,31 +169,6 @@
(f))
#js {:once true}))
(defonce ^:private schedule-canvas-snapshot-capture!
(fns/debounce
(fn []
(when (and (initialized?)
(some? wasm/canvas))
(-> (webgl/capture-canvas-snapshot-url)
(p/catch (fn [_] nil)))))
snapshot-capture-debounce-ms))
(defn- start-canvas-snapshot-listener!
[]
(when-let [prev @snapshot-tiles-handler*]
(.removeEventListener ^js ug/document "penpot:wasm:tiles-complete" prev))
(let [handler (fn [_] (schedule-canvas-snapshot-capture!))]
(reset! snapshot-tiles-handler* handler)
(.addEventListener ^js ug/document "penpot:wasm:tiles-complete" handler)))
(defn- stop-canvas-snapshot-listener!
[]
(when-let [prev @snapshot-tiles-handler*]
(.removeEventListener ^js ug/document "penpot:wasm:tiles-complete" prev))
(reset! snapshot-tiles-handler* nil)
(when-let [cancel (unchecked-get schedule-canvas-snapshot-capture! "cancel")]
(cancel)))
(defn text-editor-wasm?
[]
(or (contains? cf/flags :feature-text-editor-wasm)
@ -2072,7 +2043,6 @@
(when can-listen?
(.addEventListener canvas "webglcontextlost" on-webgl-context-lost)
(.addEventListener canvas "webglcontextrestored" on-webgl-context-restored))
(start-canvas-snapshot-listener!)
(reset! wasm/context-lost? false)
(set! wasm/context-initialized? true)))
@ -2099,7 +2069,6 @@
(when wasm/canvas
(.removeEventListener wasm/canvas "webglcontextlost" on-webgl-context-lost)
(.removeEventListener wasm/canvas "webglcontextrestored" on-webgl-context-restored))
(stop-canvas-snapshot-listener!)
(when (wasm/module-ready?)
(free-gpu-resources)

View File

@ -134,11 +134,24 @@ void main() {
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) nil)
(.deleteTexture ^js gl texture))))
(defn capture-canvas-snapshot-url
"Captures the current viewport canvas as a PNG `blob:` URL and stores it in
`wasm/canvas-snapshot-url`.
;; Codec for the transition snapshot. The snapshot is only ever shown as a
;; heavily-blurred overlay (see TRANSITION_BLUR_RADIUS), so a lossy, alpha-capable
;; codec is fine and encodes much faster than lossless PNG -- on a full-viewport
;; canvas, PNG `toBlob` of millions of pixels costs ~1s+ on the main thread.
;; WebP keeps the alpha channel (unlike JPEG) and encodes in a fraction of that.
(def ^:private SNAPSHOT_MIME "image/webp")
(def ^:private SNAPSHOT_QUALITY 0.6)
Returns a promise resolving to the URL string (or nil)."
;; The snapshot is only shown heavily blurred, so it is downscaled to this max
;; side before encoding to keep the WebP encode cheap on big/high-DPI canvases.
(def ^:private SNAPSHOT_MAX_DIM 1024)
(defonce ^:private snapshot-scratch-canvas
(delay (js/document.createElement "canvas")))
(defn capture-canvas-snapshot-url
"Captures the current viewport canvas as a downscaled WebP `blob:` URL and
stores it in `wasm/canvas-snapshot-url`. Returns a promise of the URL or nil."
[]
(if-let [^js canvas wasm/canvas]
(p/create
@ -148,14 +161,26 @@ void main() {
(when (and (string? prev) (.startsWith ^js prev "blob:"))
(js/URL.revokeObjectURL prev)))
(set! wasm/canvas-snapshot-url nil)
(.toBlob canvas
(fn [^js blob]
(if blob
(let [url (js/URL.createObjectURL blob)]
(set! wasm/canvas-snapshot-url url)
(resolve url))
(resolve nil)))
"image/png")))
(let [cw (.-width canvas)
ch (.-height canvas)
;; Cap the longest side to SNAPSHOT_MAX_DIM (never upscale).
scale (min 1.0 (/ SNAPSHOT_MAX_DIM (max 1 cw ch)))
tw (max 1 (js/Math.round (* cw scale)))
th (max 1 (js/Math.round (* ch scale)))
^js sc @snapshot-scratch-canvas
^js ctx (.getContext sc "2d")]
(set! (.-width sc) tw)
(set! (.-height sc) th)
(.drawImage ctx canvas 0 0 tw th)
(.toBlob sc
(fn [^js blob]
(if blob
(let [url (js/URL.createObjectURL blob)]
(set! wasm/canvas-snapshot-url url)
(resolve url))
(resolve nil)))
SNAPSHOT_MIME
SNAPSHOT_QUALITY))))
(p/resolved nil)))
(defn draw-thumbnail-to-canvas