@@ -2,7 +2,7 @@ import { createContextProvider } from "@solid-primitives/context";
22import { trackStore } from "@solid-primitives/deep" ;
33import { debounce , throttle } from "@solid-primitives/scheduled" ;
44import { makePersisted } from "@solid-primitives/storage" ;
5- import { convertFileSrc } from "@tauri-apps/api/core" ;
5+ import { convertFileSrc , invoke } from "@tauri-apps/api/core" ;
66import {
77 createEffect ,
88 createResource ,
@@ -23,9 +23,14 @@ import {
2323 type ProjectConfiguration ,
2424 type XY ,
2525} from "~/utils/tauri" ;
26+ import { calculateImageTransform } from "./layout" ;
2627
2728const NV12_FORMAT_MAGIC = 0x4e563132 ;
2829
30+ type ScreenshotFrameData = FrameData & {
31+ revision : number ;
32+ } ;
33+
2934function convertNv12ToRgba (
3035 nv12Data : Uint8ClampedArray ,
3136 width : number ,
@@ -184,7 +189,12 @@ function createScreenshotEditorContext() {
184189 open : false ,
185190 } ) ;
186191
187- const [ latestFrame , setLatestFrame ] = createLazySignal < FrameData > ( ) ;
192+ const [ latestFrame , setLatestFrame ] = createLazySignal < ScreenshotFrameData > ( ) ;
193+ const [ previewCanvas , setPreviewCanvas ] =
194+ createSignal < HTMLCanvasElement | null > ( null ) ;
195+ const [ previewMaskCanvas , setPreviewMaskCanvas ] =
196+ createSignal < HTMLCanvasElement | null > ( null ) ;
197+ const [ configRevision , setConfigRevision ] = createSignal ( 0 ) ;
188198 const [ originalImageSize , setOriginalImageSize ] = createSignal < {
189199 width : number ;
190200 height : number ;
@@ -234,6 +244,7 @@ function createScreenshotEditorContext() {
234244 width : img . naturalWidth ,
235245 height : img . naturalHeight ,
236246 bitmap,
247+ revision : 0 ,
237248 } ) ;
238249 setIsRenderReady ( true ) ;
239250 } catch ( e : unknown ) {
@@ -271,6 +282,7 @@ function createScreenshotEditorContext() {
271282
272283 let width : number ;
273284 let height : number ;
285+ let revision : number ;
274286 let processedData : Uint8ClampedArray ;
275287
276288 if ( isNv12Format ) {
@@ -281,6 +293,7 @@ function createScreenshotEditorContext() {
281293 const yStride = meta . getUint32 ( 0 , true ) ;
282294 height = meta . getUint32 ( 4 , true ) ;
283295 width = meta . getUint32 ( 8 , true ) ;
296+ revision = meta . getUint32 ( 12 , true ) ;
284297
285298 if ( ! width || ! height ) return ;
286299
@@ -298,6 +311,7 @@ function createScreenshotEditorContext() {
298311 const strideBytes = meta . getUint32 ( 0 , true ) ;
299312 height = meta . getUint32 ( 4 , true ) ;
300313 width = meta . getUint32 ( 8 , true ) ;
314+ revision = meta . getUint32 ( 12 , true ) ;
301315
302316 if ( ! width || ! height ) return ;
303317
@@ -333,7 +347,7 @@ function createScreenshotEditorContext() {
333347 if ( existing ?. bitmap && existing . bitmap !== bitmap ) {
334348 existing . bitmap . close ( ) ;
335349 }
336- setLatestFrame ( { width, height, bitmap } ) ;
350+ setLatestFrame ( { width, height, bitmap, revision } ) ;
337351 } catch { }
338352 } ;
339353
@@ -362,16 +376,31 @@ function createScreenshotEditorContext() {
362376 const FPS = 60 ;
363377 const FRAME_TIME = 1000 / FPS ;
364378
365- const doRenderUpdate = ( config : ProjectConfiguration ) => {
366- commands . updateScreenshotConfig ( config , false ) ;
379+ const doRenderUpdate = ( {
380+ config,
381+ revision,
382+ } : {
383+ config : ProjectConfiguration ;
384+ revision : number ;
385+ } ) => {
386+ void invoke ( "update_screenshot_config" , { config, save : false , revision } ) ;
367387 } ;
368388
369389 const throttledRenderUpdate = throttle ( doRenderUpdate , FRAME_TIME ) ;
370390 const trailingRenderUpdate = debounce ( doRenderUpdate , FRAME_TIME + 16 ) ;
371391
372- const saveConfig = debounce ( ( config : ProjectConfiguration ) => {
373- commands . updateScreenshotConfig ( config , true ) ;
374- } , 1000 ) ;
392+ const saveConfig = debounce (
393+ ( {
394+ config,
395+ revision,
396+ } : {
397+ config : ProjectConfiguration ;
398+ revision : number ;
399+ } ) => {
400+ void invoke ( "update_screenshot_config" , { config, save : true , revision } ) ;
401+ } ,
402+ 1000 ,
403+ ) ;
375404
376405 createEffect (
377406 on (
@@ -387,58 +416,17 @@ function createScreenshotEditorContext() {
387416 ...unwrap ( project ) ,
388417 annotations : unwrap ( annotations ) ,
389418 } ;
419+ const revision = configRevision ( ) + 1 ;
420+
421+ setConfigRevision ( revision ) ;
390422
391- throttledRenderUpdate ( config ) ;
392- trailingRenderUpdate ( config ) ;
393- saveConfig ( config ) ;
423+ throttledRenderUpdate ( { config, revision } ) ;
424+ trailingRenderUpdate ( { config, revision } ) ;
425+ saveConfig ( { config, revision } ) ;
394426 } ,
395427 ) ,
396428 ) ;
397429
398- const SCREEN_MAX_PADDING = 0.4 ;
399-
400- const calculateImageTransform = (
401- frameSize : { width : number ; height : number } ,
402- imageSize : { width : number ; height : number } ,
403- padding : number ,
404- crop : { position : XY < number > ; size : XY < number > } | null ,
405- ) => {
406- const cropWidth = crop ?. size . x ?? imageSize . width ;
407- const cropHeight = crop ?. size . y ?? imageSize . height ;
408- const croppedAspect = cropWidth / cropHeight ;
409- const outputAspect = frameSize . width / frameSize . height ;
410-
411- const paddingFactor = ( padding / 100.0 ) * SCREEN_MAX_PADDING ;
412- const cropBasis = Math . max ( cropWidth , cropHeight ) ;
413- const paddingPixels = cropBasis * paddingFactor ;
414-
415- const availableWidth = frameSize . width - 2 * paddingPixels ;
416- const availableHeight = frameSize . height - 2 * paddingPixels ;
417-
418- const isHeightConstrained = croppedAspect <= outputAspect ;
419-
420- let targetWidth : number ;
421- let targetHeight : number ;
422- if ( isHeightConstrained ) {
423- targetHeight = availableHeight ;
424- targetWidth = availableHeight * croppedAspect ;
425- } else {
426- targetWidth = availableWidth ;
427- targetHeight = availableWidth / croppedAspect ;
428- }
429-
430- const targetOffsetX = ( frameSize . width - targetWidth ) / 2 ;
431- const targetOffsetY = ( frameSize . height - targetHeight ) / 2 ;
432-
433- const offsetX = isHeightConstrained ? targetOffsetX : paddingPixels ;
434- const offsetY = isHeightConstrained ? paddingPixels : targetOffsetY ;
435-
436- return {
437- offset : { x : offsetX , y : offsetY } ,
438- size : { width : targetWidth , height : targetHeight } ,
439- } ;
440- } ;
441-
442430 let prevState : {
443431 frameSize : { width : number ; height : number } ;
444432 imageSize : { width : number ; height : number } ;
@@ -658,6 +646,11 @@ function createScreenshotEditorContext() {
658646 dialog,
659647 setDialog,
660648 latestFrame,
649+ previewCanvas,
650+ setPreviewCanvas,
651+ previewMaskCanvas,
652+ setPreviewMaskCanvas,
653+ configRevision,
661654 originalImageSize,
662655 isRenderReady,
663656 isImageFileReady,
0 commit comments