first commit
This commit is contained in:
commit
ff482ebe23
251 files changed
+19126
No files matched your search
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
const {PNG}=require('pngjs');
|
||||
const {inflateSync}=require('node:zlib');
|
||||
const {visualFingerprint}=require('./visual-hash.cjs');
|
||||
const SIGNATURE=Buffer.from([137,80,78,71,13,10,26,10]);
|
||||
function decodePNG(input){
|
||||
const bytes=Buffer.from(input);
|
||||
if(bytes.length<45||bytes.length>10*1024*1024||!bytes.subarray(0,8).equals(SIGNATURE)||bytes.readUInt32BE(8)!==13||bytes.toString('ascii',12,16)!=='IHDR')throw Error('Invalid bounded PNG reference');
|
||||
const width=bytes.readUInt32BE(16),height=bytes.readUInt32BE(20),depth=bytes[24],type=bytes[25];
|
||||
if(width<16||height<16||width>8192||height>8192||width*height>8*1024*1024||depth!==8||![0,2,4,6].includes(type)||bytes[26]!==0||bytes[27]!==0||bytes[28]!==0)throw Error('Unsupported PNG dimensions or encoding');
|
||||
const parts=[];let ended=false;
|
||||
for(let offset=8;offset<bytes.length;){
|
||||
if(offset+12>bytes.length)throw Error('Truncated PNG');
|
||||
const length=bytes.readUInt32BE(offset),chunk=bytes.toString('ascii',offset+4,offset+8),next=offset+12+length;
|
||||
if(next>bytes.length||ended||(chunk==='IHDR'&&offset!==8)||['acTL','fcTL','fdAT'].includes(chunk))throw Error('Unsupported PNG structure');
|
||||
if(chunk==='IDAT')parts.push(bytes.subarray(offset+8,offset+8+length));
|
||||
if(chunk==='IEND'){if(length!==0||next!==bytes.length)throw Error('Invalid PNG end');ended=true;}
|
||||
offset=next;
|
||||
}
|
||||
if(!ended||!parts.length)throw Error('Incomplete PNG');
|
||||
const channels={0:1,2:3,4:2,6:4}[type],expected=(width*channels+1)*height;
|
||||
const inflated=inflateSync(Buffer.concat(parts),{maxOutputLength:expected+1});
|
||||
try{if(inflated.length!==expected)throw Error('PNG data size differs from header');}finally{inflated.fill(0);}
|
||||
const image=PNG.sync.read(bytes,{checkCRC:true});
|
||||
if(image.width!==width||image.height!==height||image.data.length!==width*height*4)throw Error('PNG output dimensions invalid');
|
||||
return {rgba:image.data,width,height};
|
||||
}
|
||||
function fingerprintPNG(bytes){const image=decodePNG(bytes);try{return {...visualFingerprint(image),width:image.width,height:image.height};}finally{image.rgba.fill(0);}}
|
||||
module.exports={decodePNG,fingerprintPNG};
|
||||
Reference in new issue
Block a user