| Server IP : 217.154.3.148 / Your IP : 216.73.216.90 Web Server : Apache System : Linux blissful-wright.217-154-3-148.plesk.page 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 User : gracious-boyd_hfhh1h8vo9n ( 10000) PHP Version : 8.3.31 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/pages/ |
Upload File : |
/* eslint-disable @next/next/no-img-element -- debug view renders raw data URLs */
import React, {useEffect, useMemo, useRef, useState} from 'react'
import { renderAll, drawMaskTint } from '../components/MaskLayer'
type AnalyzeResp = {
ok: boolean
image?: { width: number; height: number }
tile_w_px?: number; tile_h_px?: number; grout_px?: number
grid?: { vlines:number[]; hlines:number[] }
source?: string
fg_mask?: string
}
function drawChecker(ctx: CanvasRenderingContext2D, w:number, h:number, tw=80, th=40) {
// simples Schachbrett/Stripe-Mix als Demo-Pattern
for (let y=0; y<h; y+=th) {
for (let x=0; x<w; x+=tw) {
if (((x/tw)+(y/th)) % 2 === 0) {
ctx.fillRect(x, y, tw, th)
}
}
}
}
function drawGridLines(ctx: CanvasRenderingContext2D, grid?: { vlines:number[]; hlines:number[] }) {
if (!grid) return
ctx.save()
ctx.globalAlpha = 0.25
ctx.lineWidth = 1
ctx.strokeStyle = 'black'
ctx.beginPath()
grid.vlines?.forEach(x => { ctx.moveTo(x+0.5, 0); ctx.lineTo(x+0.5, ctx.canvas.height) })
grid.hlines?.forEach(y => { ctx.moveTo(0, y+0.5); ctx.lineTo(ctx.canvas.width, y+0.5) })
ctx.stroke()
ctx.restore()
}
export default function VisualizerBlur() {
const [imgUrl, setImgUrl] = useState<string>('')
const [resp, setResp] = useState<AnalyzeResp | null>(null)
const [alpha, setAlpha] = useState(0.55)
const [blurPx, setBlurPx] = useState(4)
const [showGrout, setShowGrout] = useState(true)
const [showTint, setShowTint] = useState(false)
const bgRef = useRef<HTMLCanvasElement>(null)
const fgRef = useRef<HTMLCanvasElement>(null)
const imgRef = useRef<HTMLImageElement>(null)
const tileW = resp?.tile_w_px ?? 80
const tileH = resp?.tile_h_px ?? 40
// Pattern-Fn abhängig von tileW/H
const patternFn = useMemo(() => {
return (ctx:CanvasRenderingContext2D, w:number, h:number) => {
ctx.fillStyle = '#aaa' // wird über globalAlpha geregelt
drawChecker(ctx, w, h, tileW, tileH)
}
}, [tileW, tileH])
useEffect(() => {
const run = async () => {
if (!imgUrl || !resp?.fg_mask) return
const bg = bgRef.current!, fg = fgRef.current!, img = imgRef.current!
await renderAll({
bgCanvas: bg,
fgCanvas: fg,
originalImg: img,
maskDataUrl: resp.fg_mask!,
drawPatternFn: patternFn,
drawGroutFn: showGrout ? (ctx) => drawGridLines(ctx, resp?.grid) : undefined,
overlayAlpha: alpha,
blurPx
})
if (showTint) {
await drawMaskTint(fg, resp.fg_mask!, 'rgba(0,255,0,0.20)')
}
}
run()
}, [imgUrl, resp, alpha, blurPx, showGrout, showTint, patternFn])
async function onFile(e: React.ChangeEvent<HTMLInputElement>) {
const f = e.target.files?.[0]
if (!f) return
const url = URL.createObjectURL(f)
setImgUrl(url)
const form = new FormData()
form.append('file', f)
// seg=1 + weichere Werte
const res = await fetch(`/nextjs/api/analyze?seg=1&maskw=0.70&maskblur=3.5`, { method:'POST', body: form })
const json = (await res.json()) as AnalyzeResp
setResp(json)
}
return (
<div style={{padding:'12px', fontFamily:'ui-sans-serif, system-ui'}}>
<div style={{display:'flex', gap:16, alignItems:'center', flexWrap:'wrap'}}>
<input type="file" accept="image/*" onChange={onFile}/>
<label>Alpha
<input type="range" min={0.2} max={0.9} step={0.05} value={alpha}
onChange={e=>setAlpha(parseFloat(e.target.value))}
style={{verticalAlign:'middle', marginLeft:8}}/>
</label>
<label>Mask Blur
<input type="range" min={0} max={8} step={1} value={blurPx}
onChange={e=>setBlurPx(parseInt(e.target.value))}
style={{verticalAlign:'middle', marginLeft:8}}/>
</label>
<label><input type="checkbox" checked={showGrout} onChange={e=>setShowGrout(e.target.checked)}/> Fugen zeigen</label>
<label><input type="checkbox" checked={showTint} onChange={e=>setShowTint(e.target.checked)}/> Maske grün debuggen</label>
</div>
<div style={{marginTop:8, color:'#4b5563', fontSize:12}}>
{resp?.ok && (
<div>
Service: tile_w / tile_h / grout — <b>{resp.tile_w_px ?? '-'}</b> / <b>{resp.tile_h_px ?? '-'}</b> / <b>{resp.grout_px ?? '-'}</b><br/>
<span style={{color:'#10b981'}}>Mask ready</span> • {resp.source}
</div>
)}
</div>
<div style={{position:'relative', display:'inline-block', marginTop:12}}>
{/* verstecktes Original für Maße */}
{imgUrl && <img ref={imgRef} src={imgUrl} style={{display:'none'}} alt="src" onLoad={()=>{ /* re-render handled by useEffect */ }}/>}
<canvas ref={bgRef} style={{position:'absolute', inset:0}} />
<canvas ref={fgRef} style={{position:'absolute', inset:0}} />
</div>
</div>
)
}