feat: preview and check large PNG sheets by reading them as a stream
All checks were successful
Build and deploy / Validate source (push) Successful in 6s
Build and deploy / Integration suite on a real stack (push) Successful in 2m33s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 1m0s

A large sheet had no preview and no automatic check of pieces, residue,
gaps and background, because decoding it whole would crash the browser.
A PNG over 150 MB is now inflated as a stream (DecompressionStream) and
unfiltered row by row, keeping every n-th pixel: a 600-pixel-wide copy in
about 100 MB of memory whatever the file's size (6.6 s for 500 MB, 22.7 s
for 2.4 GB of random pixels, identical to Pillow's output for RGB, RGBA,
grey, grey+alpha, palette and 16-bit). The copy is the preview and what
the sheet check reads; the grade still comes at once from the header.
The preview note promising a server-side re-nesting that does not exist
is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-29 15:25:13 -03:00
parent 9e69c48d2d
commit 7116ebe50a
3 changed files with 128 additions and 4 deletions

View File

@@ -75,11 +75,25 @@ function sel(fs){
if(md && md.dpiFolha!=null && md.dpiFolha<DPI_RECUSA) return recusaFolha(x, md);
x.med=md; if(md) x.m=md.alt/100; x.pct=55; pintaFolha();
if(md && md.grande){
// Graded from its size in pixels; the pieces, gaps and background
// are checked by the team, since the image is never opened here.
// Graded at once from its size in pixels. A PNG is then read as a
// stream into a small copy: the preview, and the sheet check.
x.an={dpi:md.dpiFolha, grande:true, artes:0, residuos:0, encostadas:0,
fundoChapado:false, aproveitamento:0, alturaCm:md.alt, fonteDpi:'arquivo'};
x.pct=null; pintaFolha();
if(!/\.png$/i.test(x.f.name)){ x.pct=null; pintaFolha(); return; }
x.pct=60; pintaFolha();
let mostrado=0;
previaPngGrande(x.f, 600, p=>{
const pct=60+Math.floor(p*38);
if(pct-mostrado>=3){ mostrado=pct; x.pct=pct; pintaFolha(); }
}).then(cv=>{
if(cv && folhas.includes(x)){
x.previewSrc=cv.toDataURL('image/webp',0.85);
try{
const an=analisarFolha(cv, md.larg);
x.an={...an, dpi:md.dpiFolha, fonteDpi:'arquivo', grande:false};
}catch(e){}
}
}).catch(()=>{}).finally(()=>{ x.pct=null; pintaFolha(); });
}else if(md && RENDERIZA.test(x.f.name)){
carregarImagem(x.f).then(img=>{
if(img){ x.previewSrc=img.src; try{ x.an=analisarFolha(img, md.larg); if(x.an) x.an.fonteDpi='arquivo'; }