feat: add view transform, photo loading and download helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
immortaly007andClaude Opus 5.5 committed 2026-10-15 14:45:38 +02:00
1 parent 0a97cb88af
commit 99cafc0b55
5 files changed
+102

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
export function downloadText(filename: string, text: string, mime: string): void {
const url = URL.createObjectURL(new Blob([text], { type: mime }));
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
+21
View File
@@ -0,0 +1,21 @@
import type { RasterImage } from './types';
export const MAX_SIDE = 2000;
export function fitSize(width: number, height: number, maxSide: number): { width: number; height: number } {
const s = Math.min(1, maxSide / Math.max(width, height));
return { width: Math.round(width * s), height: Math.round(height * s) };
}
export async function loadPhoto(file: Blob): Promise<RasterImage> {
const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' });
try {
const { width, height } = fitSize(bitmap.width, bitmap.height, MAX_SIDE);
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d')!;
ctx.drawImage(bitmap, 0, 0, width, height);
return { width, height, data: ctx.getImageData(0, 0, width, height).data };
} finally {
bitmap.close();
}
}
+25
View File
@@ -0,0 +1,25 @@
import type { Point } from './types';
export type View = { scale: number; offsetX: number; offsetY: number };
const MIN_SCALE = 0.05;
const MAX_SCALE = 20;
export function screenToImage(p: Point, view: View): Point {
return { x: (p.x - view.offsetX) / view.scale, y: (p.y - view.offsetY) / view.scale };
}
export function zoomAt(view: View, screenPoint: Point, factor: number): View {
const scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, view.scale * factor));
const anchor = screenToImage(screenPoint, view);
return { scale, offsetX: screenPoint.x - anchor.x * scale, offsetY: screenPoint.y - anchor.y * scale };
}
export function fitView(imageWidth: number, imageHeight: number, viewWidth: number, viewHeight: number, padding = 16): View {
const scale = Math.min((viewWidth - 2 * padding) / imageWidth, (viewHeight - 2 * padding) / imageHeight);
return {
scale,
offsetX: (viewWidth - imageWidth * scale) / 2,
offsetY: (viewHeight - imageHeight * scale) / 2,
};
}