feat: pack large artworks in Artes avulsas from a streamed copy
Some checks failed
Build and deploy / Validate source (push) Successful in 6s
Build and deploy / Integration suite on a real stack (push) Failing after 2m36s
Build and deploy / Secret scan and release gate (push) Successful in 8s
Build and deploy / Publish images (push) Has been skipped

Artes avulsas decoded every file whole, and the packing decoded each one
again on every repaint, so a large artwork failed to load. A PNG over 150
MB is now measured from its header (pixel size, so DPI and grade stay
exact) and packed from a 1200-pixel copy read as a stream, with progress on
its card; every artwork's image is loaded once and reused by the packing.
A large JPG or WebP asks for a PNG, and the grade card no longer calls a
file unreadable while it is still being read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-29 16:02:35 -03:00
parent 64e47ee481
commit f4aacb5776
4 changed files with 51 additions and 4 deletions

View File

@@ -12,6 +12,18 @@ function carregarImagem(file){
fr.readAsDataURL(file);
});
}
// The image each artwork was measured with, loaded once and reused on every
// repaint: the packing used to read and decode the whole file again each
// time. Kept outside the artwork, which is saved with the cart.
const imagensDasArtes=new WeakMap();
function imagemDaArte(a){
if(!a.src) return Promise.resolve(null);
const guardada=imagensDasArtes.get(a);
if(guardada && guardada.src===a.src) return guardada.img;
const img=new Promise(res=>{ const im=new Image(); im.onload=()=>res(im); im.onerror=()=>res(null); im.src=a.src; });
imagensDasArtes.set(a,{src:a.src,img});
return img;
}
// A folha precisa parecer uma folha: borda, largura marcada e régua de metros.
// Sem isso o cliente não tem noção de escala e acha que está mal encaixado.
function molduraFolha(ctx,W,H,larguraCm,metrosReais){
@@ -199,12 +211,14 @@ function desenhaMontagem(area,lista,mts,W,fora,aoTerminar){
return;
}
Promise.all(itens.map(a=>carregarImagem(a.f).then(img=>({a,img})))).then(imagens=>{
Promise.all(itens.map(a=>imagemDaArte(a).then(img=>({a,img})))).then(imagens=>{
if(area.montagemVersao!==versao) return;
const cargas=imagens.flatMap(c=>Array.from({length:c.a.q||1},(_,copyIndex)=>({...c,copyIndex})));
cargas.forEach(c=>{
c.w = c.a.cm; // o motor trabalha em centímetros
c.h = c.w * (c.img ? (c.a.giro? c.img.width/c.img.height : c.img.height/c.img.width) : 1);
// The file's own proportion: a large artwork is drawn from a smaller copy.
const prop = c.a.prop>0 ? c.a.prop : c.img ? c.img.height/c.img.width : 1;
c.h = c.w * (c.a.giro? 1/prop : prop);
c.travado = !!c.a.giro; // giro do cliente manda no encaixe
});
const enc=encaixar(cargas,filme);