Skip to content

Commit 3bfa4bd

Browse files
committed
feat(desktop): sync screenshot preview frames with config revision
Made-with: Cursor
1 parent 9c4ea8c commit 3bfa4bd

1 file changed

Lines changed: 48 additions & 55 deletions

File tree

apps/desktop/src/routes/screenshot-editor/context.tsx

Lines changed: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createContextProvider } from "@solid-primitives/context";
22
import { trackStore } from "@solid-primitives/deep";
33
import { debounce, throttle } from "@solid-primitives/scheduled";
44
import { makePersisted } from "@solid-primitives/storage";
5-
import { convertFileSrc } from "@tauri-apps/api/core";
5+
import { convertFileSrc, invoke } from "@tauri-apps/api/core";
66
import {
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

2728
const NV12_FORMAT_MAGIC = 0x4e563132;
2829

30+
type ScreenshotFrameData = FrameData & {
31+
revision: number;
32+
};
33+
2934
function 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

Comments
 (0)