diff --git a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs index 1f343ff3df..74c8c70cbe 100644 --- a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs @@ -17,6 +17,7 @@ [app.main.rasterizer :as thr] [app.main.store :as st] [app.main.ui.css-cursors :as cur] + [app.render-wasm.api :as wasm.api] [app.util.dom :as dom] [app.util.keyboard :as kbd] [app.util.object :as obj] @@ -50,7 +51,7 @@ (obj/set! internal-state "canvas" new-canvas) new-canvas)))))))) -(defn process-pointer-move [viewport-node canvas canvas-image-data zoom-view-context client-x client-y] +(defn process-pointer-move [viewport-node canvas canvas-image-data zoom-view-context client-x client-y use-dpr?] (when-let [image-data (mf/ref-val canvas-image-data)] (when-let [zoom-view-node (dom/get-element "picker-detail")] (when-not (mf/ref-val zoom-view-context) @@ -58,12 +59,15 @@ (let [canvas-width 260 canvas-height 140 {brx :left bry :top} (dom/get-bounding-rect viewport-node) - x (mth/floor (- client-x brx)) y (mth/floor (- client-y bry)) + dpr (if use-dpr? wasm.api/dpr 1) + canvas-x (* x dpr) + canvas-y (* y dpr) + zoom-context (mf/ref-val zoom-view-context) - offset (* (+ (* y (unchecked-get image-data "width")) x) 4) + offset (* (+ (* canvas-y (unchecked-get image-data "width")) canvas-x) 4) rgba (unchecked-get image-data "data") r (obj/get rgba (+ 0 offset)) @@ -71,8 +75,8 @@ b (obj/get rgba (+ 2 offset)) a (obj/get rgba (+ 3 offset)) - sx (- x 32) - sy (if (cfg/check-browser? :safari) y (- y 17)) + sx (- canvas-x 32) + sy (if (cfg/check-browser? :safari) canvas-y (- canvas-y 17)) sw 65 sh 35 dx 0 @@ -165,7 +169,7 @@ (mf/use-callback (mf/deps viewport-node) (fn [event] - (process-pointer-move viewport-node canvas canvas-image-data zoom-view-context (.-clientX event) (.-clientY event))))] + (process-pointer-move viewport-node canvas canvas-image-data zoom-view-context (.-clientX event) (.-clientY event) false)))] (when (obj/get canvas-context "imageSmoothingEnabled") (obj/set! canvas-context "imageSmoothingEnabled" false)) @@ -201,7 +205,109 @@ (fn [] (when canvas-ready (let [{:keys [x y]} @initial-mouse-pos] - (process-pointer-move viewport-node canvas canvas-image-data zoom-view-context x y))))) + (process-pointer-move viewport-node canvas canvas-image-data zoom-view-context x y false))))) + + [:div {:id "pixel-overlay" + :tab-index 0 + :class (dm/str (cur/get-static "picker") " " (stl/css :pixel-overlay)) + :on-pointer-down handle-pointer-down-picker + :on-pointer-up handle-pointer-up-picker + :on-pointer-move handle-pointer-move-picker + :on-mouse-enter handle-mouse-enter}])) + +(mf/defc pixel-overlay-wasm* + {::mf/wrap-props false} + [{:keys [viewport-ref canvas-ref] :rest props}] + (let [viewport-node (mf/ref-val viewport-ref) + canvas (mf/ref-val canvas-ref) + canvas-context (when (some? canvas) (.getContext canvas "webgl2" #js {:willReadFrequently true})) + canvas-image-data (mf/use-ref nil) + zoom-view-context (mf/use-ref nil) + canvas-ready? (some? (mf/ref-val canvas-image-data)) + initial-mouse-pos (mf/use-state {:x 0 :y 0}) + update-str (rx/subject) + + handle-keydown + (mf/use-callback + (fn [event] + (when (kbd/esc? event) + (dom/stop-propagation event) + (dom/prevent-default event) + (st/emit! (dwc/stop-picker)) + (modal/disallow-click-outside!)))) + + handle-pointer-down-picker + (mf/use-callback + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dwu/start-undo-transaction :mouse-down-picker) + (dwc/pick-color-select true (kbd/shift? event))))) + + handle-pointer-up-picker + (mf/use-callback + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dwu/commit-undo-transaction :mouse-down-picker) + (dwc/stop-picker)) + (modal/disallow-click-outside!))) + + handle-draw-picker-canvas + (mf/use-callback + (mf/deps canvas-context) + (fn [] + (when (some? canvas-context) + (let [width (unchecked-get canvas "width") + height (unchecked-get canvas "height") + buffer (js/Uint8ClampedArray. (* width height 4)) + _ (.readPixels canvas-context 0 0 width height (.-RGBA canvas-context) (.-UNSIGNED_BYTE canvas-context) buffer) + image-data (js/ImageData. buffer width height)] + (mf/set-ref-val! canvas-image-data image-data))))) + + handle-canvas-changed + (mf/use-callback + (fn [_] + (rx/push! update-str :update))) + + handle-mouse-enter + (mf/use-callback + (mf/deps viewport-node) + (fn [event] + (let [x (.-clientX event) + y (.-clientY event)] + (reset! initial-mouse-pos {:x x + :y y})))) + handle-pointer-move-picker + (mf/use-callback + (mf/deps viewport-node) + (fn [event] + (process-pointer-move viewport-node canvas canvas-image-data zoom-view-context (.-clientX event) (.-clientY event) true)))] + + (mf/use-effect + (fn [] + (let [listener (events/listen js/document EventType.KEYDOWN handle-keydown)] + #(events/unlistenByKey listener)))) + + (mf/use-effect + (fn [] + (let [sub (->> update-str + (rx/debounce 10) + (rx/subs! handle-draw-picker-canvas))] + #(rx/dispose! sub)))) + + (mf/use-effect + (fn [] + (handle-canvas-changed nil) + (let [_ (js/document.addEventListener "wasm:render" handle-canvas-changed)] + #(js/document.removeEventListener "wasm:render" handle-canvas-changed)))) + + (mf/use-effect + (mf/deps viewport-node canvas-ready?) + (fn [] + (when canvas-ready? + (let [{:keys [x y]} @initial-mouse-pos] + (process-pointer-move viewport-node canvas canvas-image-data zoom-view-context x y true))))) [:div {:id "pixel-overlay" :tab-index 0 diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 17b65398a5..6d98925a35 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -387,10 +387,8 @@ :zoom zoom}]) (when picking-color? - [:& pixel-overlay/pixel-overlay {:vport vport - :vbox vbox - :layout layout - :viewport-ref viewport-ref}])] + [:> pixel-overlay/pixel-overlay-wasm* {:viewport-ref viewport-ref + :canvas-ref canvas-ref}])] [:canvas {:id "render" :data-testid "canvas-wasm-shapes" diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 2f9fc38f0b..c3600b2f60 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -100,7 +100,10 @@ (defn- render [timestamp] (h/call wasm/internal-module "_render" timestamp) - (set! wasm/internal-frame-id nil)) + (set! wasm/internal-frame-id nil) + ;; emit custom event + (let [event (js/CustomEvent. "wasm:render")] + (js/document.dispatchEvent ^js event))) (def debounce-render (fns/debounce render 100))