import { useEffect, useRef } from 'react'; import Box from '@mui/material/Box'; import { thumbHashToRGBA } from 'thumbhash'; export function decodeThumbHash(thumbHash) { if (!thumbHash || typeof thumbHash !== 'string') return null; try { const bytes = Uint8Array.from(atob(thumbHash), (c) => c.charCodeAt(0)); return thumbHashToRGBA(bytes); } catch (err) { console.warn('[ThumbHashImage] decode failed:', err); return null; } } export function getThumbHashDimensions(thumbHash) { const decoded = decodeThumbHash(thumbHash); if (!decoded) return null; return { w: decoded.w, h: decoded.h }; } function paintThumbHash(canvas, thumbHash, explicitWidth, explicitHeight) { const parent = canvas.parentElement; const rect = parent ? parent.getBoundingClientRect() : null; const width = explicitWidth || rect?.width; const height = explicitHeight || rect?.height; if (!width || !height) return; const decoded = decodeThumbHash(thumbHash); if (!decoded) return; const { w, h, rgba } = decoded; const src = document.createElement('canvas'); src.width = w; src.height = h; src.getContext('2d').putImageData( new ImageData(new Uint8ClampedArray(rgba), w, h), 0, 0, ); canvas.width = Math.round(width); canvas.height = Math.round(height); const ctx = canvas.getContext('2d'); if (!ctx) return; ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(src, 0, 0, canvas.width, canvas.height); } export default function ThumbHashImage({ thumbHash, width, height, sx }) { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas || !thumbHash) return; paintThumbHash(canvas, thumbHash, width, height); const parent = canvas.parentElement; if (!parent || typeof ResizeObserver === 'undefined') return undefined; const observer = new ResizeObserver(() => { paintThumbHash(canvas, thumbHash, width, height); }); observer.observe(parent); return () => observer.disconnect(); }, [thumbHash, width, height]); return ( ); }