import { clampRect, type LayoutRect } from "./layoutProfile.js"; import type { Bitmap } from "./ocrPreprocess.js"; // EXPERIMENTAL, read-only lock-status detection (nice-to-have). Genshin shows a // padlock at the top-right of the artifact detail card: a bright gold fill when // locked, a dim outline when not. This estimates that icon region and measures // the fraction of bright "lock-gold" pixels; above a threshold the piece is // considered locked. // // The crop position and threshold need calibration against a reference 16:9 // screenshot before this is wired into the capture pipeline, so it ships pure and // unit-tested but unused by main.ts. It never drives any in-game action - it only // reads state for triage. export function lockIconCropRect(detailRect: LayoutRect, imageSize: { width: number; height: number }): LayoutRect { return clampRect( { x: Math.round(detailRect.x + detailRect.width * 0.735), y: Math.round(detailRect.y + detailRect.height * 0.355), width: Math.round(detailRect.width * 0.105), height: Math.round(detailRect.height * 0.07), }, imageSize, ); } // A gold/highlighted lock pixel: red high, green mid-high, blue low. function isLockGold(b: number, g: number, r: number): boolean { return r >= 180 && g >= 140 && b <= 120 && r > b + 40 && g > b + 20; } export function lockSignalRatio(bitmap: Bitmap): number { const { data, width, height } = bitmap; const pixels = width * height; if (pixels === 0) return 0; let gold = 0; for (let pixel = 0; pixel < pixels; pixel++) { const index = pixel * 4; if (isLockGold(data[index], data[index + 1], data[index + 2])) gold++; } return gold / pixels; } export const DEFAULT_LOCK_THRESHOLD = 0.06; export function isLocked(signalRatio: number, threshold: number = DEFAULT_LOCK_THRESHOLD): boolean { return signalRatio >= threshold; } export function detectLockState(bitmap: Bitmap, threshold: number = DEFAULT_LOCK_THRESHOLD): boolean { return isLocked(lockSignalRatio(bitmap), threshold); }