code.gitea.io/gitea@v1.22.3/web_src/js/utils/image.js (about) 1 export async function pngChunks(blob) { 2 const uint8arr = new Uint8Array(await blob.arrayBuffer()); 3 const chunks = []; 4 if (uint8arr.length < 12) return chunks; 5 const view = new DataView(uint8arr.buffer); 6 if (view.getBigUint64(0) !== 9894494448401390090n) return chunks; 7 8 const decoder = new TextDecoder(); 9 let index = 8; 10 while (index < uint8arr.length) { 11 const len = view.getUint32(index); 12 chunks.push({ 13 name: decoder.decode(uint8arr.slice(index + 4, index + 8)), 14 data: uint8arr.slice(index + 8, index + 8 + len), 15 }); 16 index += len + 12; 17 } 18 19 return chunks; 20 } 21 22 // decode a image and try to obtain width and dppx. If will never throw but instead 23 // return default values. 24 export async function imageInfo(blob) { 25 let width = 0; // 0 means no width could be determined 26 let dppx = 1; // 1 dot per pixel for non-HiDPI screens 27 28 if (blob.type === 'image/png') { // only png is supported currently 29 try { 30 for (const {name, data} of await pngChunks(blob)) { 31 const view = new DataView(data.buffer); 32 if (name === 'IHDR' && data?.length) { 33 // extract width from mandatory IHDR chunk 34 width = view.getUint32(0); 35 } else if (name === 'pHYs' && data?.length) { 36 // extract dppx from optional pHYs chunk, assuming pixels are square 37 const unit = view.getUint8(8); 38 if (unit === 1) { 39 dppx = Math.round(view.getUint32(0) / 39.3701) / 72; // meter to inch to dppx 40 } 41 } 42 } 43 } catch {} 44 } 45 46 return {width, dppx}; 47 }