diff --git a/src/utils/canvas-drawing.ts b/src/utils/canvas-drawing.ts index 1b2a368..dfb1bb9 100644 --- a/src/utils/canvas-drawing.ts +++ b/src/utils/canvas-drawing.ts @@ -1,5 +1,8 @@ import { DETECTION_TYPES } from '@/constants/detectionModeConfig'; +/** + * Color mapping for detection types + */ export const DETECTION_COLORS: Record = Object.keys(DETECTION_TYPES).reduce( (acc, key) => { acc[key] = DETECTION_TYPES[key].color; @@ -8,6 +11,41 @@ export const DETECTION_COLORS: Record = Object.keys(DETECTION_TY {} as Record, ); +// Cache for resolved colors to avoid DOM access in every frame +const resolvedColorCache = new Map(); + +/** + * Resolves a color string that might contain CSS variables or modern color formats + * into an RGB string that HTML5 Canvas understands. + */ +function resolveCanvasColor(colorStr: string): string { + if (typeof window === 'undefined') return colorStr; + + // Return from cache if already resolved + if (resolvedColorCache.has(colorStr)) { + return resolvedColorCache.get(colorStr)!; + } + + // Use a temporary element to let the browser resolve the color (handles var(), oklch(), etc.) + try { + const temp = document.createElement('div'); + temp.style.color = colorStr; + temp.style.display = 'none'; + document.body.appendChild(temp); + const resolved = getComputedStyle(temp).color; + document.body.removeChild(temp); + + if (resolved && resolved !== 'transparent') { + resolvedColorCache.set(colorStr, resolved); + return resolved; + } + } catch (e) { + console.warn('Failed to resolve color:', colorStr, e); + } + + return colorStr; +} + export const drawBoundingBoxes = ( ctx: CanvasRenderingContext2D, detections: any[], @@ -32,27 +70,33 @@ export const drawBoundingBoxes = ( const y2 = bbox.y2 * scaleY; const type = (detection.type || detection._detType || '').toLowerCase(); - const boxColor = DETECTION_COLORS[type] || '#3b82f6'; + const rawColor = DETECTION_COLORS[type] || '#3b82f6'; + const boxColor = resolveCanvasColor(rawColor); + // 1. Draw Border (Always Solid) + ctx.globalAlpha = 1.0; ctx.strokeStyle = boxColor; ctx.lineWidth = 3; ctx.strokeRect(x1, y1, x2 - x1, y2 - y1); - // Transparent fill - ctx.fillStyle = boxColor + '20'; + // 2. Draw Fill (Transparent/Light - using globalAlpha for maximum compatibility) + ctx.globalAlpha = 0.1; // 10% Opacity correctly shows the road + ctx.fillStyle = boxColor; ctx.fillRect(x1, y1, x2 - x1, y2 - y1); + // 3. Draw Label const id = detection.pothole_id ?? detection.signboard_id ?? detection.detection_id; const label = `${type.replace(/_/g, ' ')} #${id} ${(detection.confidence * 100).toFixed(0)}%`; ctx.font = 'bold 12px sans-serif'; const metrics = ctx.measureText(label); - // Label background + // Label Background (Solid for readability) + ctx.globalAlpha = 1.0; ctx.fillStyle = boxColor; ctx.fillRect(x1, y1 - 20, metrics.width + 10, 20); - // Label text + // Label Text (Solid White) ctx.fillStyle = '#fff'; ctx.fillText(label, x1 + 5, y1 - 6); });