UNPKG

@blameitonyourisp/blurrid

Version:

Generate and render blurred placeholders for lazy loaded images.

2 lines (1 loc) 13.6 kB
import e from"sharp";class t extends Error{constructor(e){super();let t="";for(const i in e)switch(i){case"name":this.name=e[i];break;case"message":t=`${e[i]}\n${t}`;break;default:{const a=`${i}: ${e[i]}`;t=`${t}${r(a,"gray",1)}\n`}}this.message=t||""}}const i=(e,{modifiers:t=[],tabs:i=0,tabSize:r=4}={})=>{let s="";for(const e of t)s=`${s}${e}`;const n=a.decorations.reset;return`${s}${" ".repeat(i*r)}${e}${n}`},r=(e,t,r=0)=>{const s=a.fgColors[t];return i(e,s?{modifiers:[s],tabs:r}:{tabs:r})},a={fgColors:{black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:"",gray:""},bgColors:{black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:"",gray:""},decorations:{reset:"",bright:"",dim:"",underline:"",blink:"",reverse:"",hidden:""}},s=(e,i)=>{if(e.length%i)throw new t({name:"ChannelError",message:"Channel length is not a multiple of row width","channel-length":e.length,"row-width":i});const r=Array.from(Array.from({length:e.length/i}),(()=>[]));for(const[t,a]of e.entries())r[Math.floor(t/i)].push(a);return r},n=e=>{const i=e[0].length,r=[];for(const a of e){if(a.length!==i)throw new t({name:"ChannelError",message:"Channel row width does not match sample width","sample-width":length,"row-width":a.length});r.push(...a)}return r};class h{#e;#t;constructor({image:e,sample:t}){this.#e=e,this.#t={...t,max:Math.max(t.width,t.height)}}resize(e){this.#e.height=Math.round(this.#e.height*e/this.#e.width),this.#e.width=e}get image(){return this.#e}get sample(){return this.#t}}class o{#i;#r;#a;#s;#n;constructor({length:e=16,size:t=Math.floor(6*e/8),buffer:i=new ArrayBuffer(t)}={}){this.#i=i,this.#r=0,this.#a=0,this.#s=0,this.#n=0}write(e,{size:t=o.#h(e),offset:i=this.#s,signed:r=!1}={}){return this.#o().append(e,{size:t,offset:i,signed:r}).isWriteable?this.#l(e,{size:t,offset:i,signed:r}):NaN}writeAbsolute(e,{offset:t=this.#s,signed:i=!1}={}){const r={value:o.#h(e),size:5};if(!this.#o().append(r.value,{...r,offset:t}).append(e,{signed:i}).isWriteable)return NaN;this.#l(r.value,{...r,offset:t});return this.#l(e,{signed:i})}writeRelative(e,{offset:t=this.#s,signed:i=!1}={}){0===e&&(e=1);const r=o.#h(e)-this.#n,a={value:r>0?1<<r>>>0:0,size:Math.abs(r)+1};if(!this.#o().append(a.value,{...a,offset:t}).append(e,{signed:i}).isWriteable)return NaN;this.#l(a.value,{...a,offset:t});return this.#l(e,{signed:i})}writeString(e,{offset:t=this.#s}={}){const i={value:o.#h(e.length),size:5};let r=this.#o().append(i.value,{...i}).append(e.length);for(let t=0;t<e.length;t++)r=r.append(0,{size:8});if(!r.isWriteable)return"";this.#l(i.value,{...i,offset:t}),this.#l(e.length);for(const t of e)this.#l(t.charCodeAt(0),{size:8});return e}read(e,{offset:t=this.#r,signed:i=!1}={}){return this.#d().append(e,{offset:t,signed:i}).isReadable?this.#f(e,{offset:t,signed:i}):NaN}readAbsolute({offset:e=this.#r,signed:t=!1}={}){const i=this.#r,r=this.#a,a=this.#d().append(5,{offset:e});let s=0;return a.isReadable&&(s=this.#f(5,{offset:e}),a.append(s,{signed:t})),a.isReadable&&s?this.#f(s,{signed:t}):(this.#r=i,this.#a=r,NaN)}readRelative({offset:e=this.#r,signed:t=!1}={}){const i=this.#r,r=this.#a;let a=1;const s=this.#d().append(1,{offset:e});s.isReadable&&(a=this.#f(1,{offset:e})?1:-1);let n=0;for(;s.append(1).isReadable&&!this.#f(1);)n++;const h=r+a*n;return s.append(h,{signed:t}),s.isReadable&&h?(this.#r--,this.#f(h,{signed:t})):(this.#r=i,this.#a=r,NaN)}readString({offset:e=this.#r}={}){const t=this.#r,i=this.#a,r=this.#d().append(5,{offset:e});let a=0;r.isReadable&&(a=this.#f(5,{offset:e})),r.append(a);let s=0;if(r.isReadable&&(s=this.#f(a)),!r.isReadable||!s)return this.#r=t,this.#a=i,"";let n="";for(let e=0;e<s;e++){if(r.append(8),!r.isReadable)return this.#r=t,this.#a=i,"";n+=String.fromCharCode(this.#f(8))}return n}copy({target:e,targetStart:i=e?.writePointer||0,sourceStart:r=0,sourceEnd:a=this.bitLength}={}){if(r<0||a>this.bitLength)throw new t({name:"BitBufferError",message:"Requested bits out of source buffer range","source-start":r,"source-end":a,"source-bit-length":this.bitLength});const s=a-r,n=Math.ceil((s+i)/8);e??=new o({size:n});const h=e.bitLength-i;if(s>h)throw new t({name:"BitBufferError",message:"Source bits exceed bits available in target buffer","source-bits":s,"target-bits":h});for(let t=0;t<s;t++)e.write(this.#f(1,{offset:r+t}),{size:1,offset:i+t});return e}toString(){let e="",t=0,i=0;const r=new Uint8Array(this.#i);for(let a=0;a<3*Math.ceil(this.byteLength/3);a++){i=(i|(r[a]||0)<<16-8*t)>>>0,t=++t%3,t||(e+=o.#g(i),i=0)}return e}#o(){const e={writeable:!0,offset:this.#s},t=(i,{size:r=o.#h(i),offset:a=e.offset,signed:s=!1}={})=>{if(e.writeable){const t=Math.abs(i),n=this.bitLength-a;e.writeable=!(!s&&i<0)&&(!(o.#h(t)>r)&&(!!Number.isInteger(t)&&(!(r<0||r>32)&&!(r+(s?1:0)>n))))}return e.offset+=r,{append:t,get isWriteable(){return e.writeable}}};return{append:t,get isWriteable(){return e.writeable}}}#l(e,{size:t=o.#h(e),offset:i=this.#s,signed:r=!1}={}){const a=Math.abs(e),{view:s,byteLength:n,subBit:h}=this.#u(t,i);for(let e=0;e<n;e++){const i=s.getUint8(e);let r=0;for(let s=0;s<8;s++){const n=8*e+s;r|=(n<h||n>h+t?i<<24+s>>>31:a<<32-t+(n-h)>>>31)<<7-s}s.setUint8(e,r)}return this.#s=i+t,r&&this.#l(e>=0?1:0,{size:1}),this.#n=t,e}#d(){const e={readable:!0,offset:this.#r},t=(i,{offset:r=e.offset,signed:a=!1}={})=>{if(e.readable){const t=this.bitLength-r;e.readable=!(i<0||i>32)&&!(i+(a?1:0)>t)}return e.offset+=i,{append:t,get isReadable(){return e.readable}}};return{append:t,get isReadable(){return e.readable}}}#f(e,{offset:t=this.#r,signed:i=!1}={}){const{view:r,byteLength:a,subBit:s}=this.#u(e,t);let n=0;for(let e=0;e<a;e++){const t=24+s-8*e;n=t>=0?(n|r.getUint8(e)<<t)>>>0:(n|r.getUint8(e)>>>-t)>>>0}this.#r=t+e;const h=i&&0===this.#f(1)?-1:1;return this.#a=e,h*(n>>>32-e)}#u(e,t){const i=Math.floor(t/8),r=t-8*i,a=Math.ceil((r+e)/8);this.byteLength;return{view:new DataView(this.#i,i,a),byteLength:a,subBit:r}}get bitLength(){return this.byteLength<<3}get byteLength(){return this.#i.byteLength}get readPointer(){return this.#r}set readPointer(e){e<0||e>this.bitLength||(this.#r=e)}get lastReadSize(){return this.#a}set lastReadSize(e){e<0||e>32||(this.#a=e)}get writePointer(){return this.#s}set writePointer(e){e<0||e>this.bitLength||(this.#s=e)}get lastWriteSize(){return this.#n}set lastWriteSize(e){e<0||e>32||(this.#n=e)}static from(e){if(!e.match(/^[A-Za-z0-9\-_]*$/))throw new t({name:"BitBufferError",message:"Encoded string is not url-safe base 64 encoded","encoded-string":e});const i=new o({size:Math.ceil(3*e.length/4)}),r=/[A-Za-z0-9\-_]{1,4}/g;for(const t of e.match(r)||[]){const e=o.#m(t.padEnd(4,"A"));i.write(e,{size:24})}return i.writePointer=0,i.readPointer=0,i}static#m(e){let t=0;for(const[i,r]of e.split("").entries()){t=(t|o.#c.indexOf(r)<<18-6*i)>>>0}return t}static#g(e){let t="";for(let i=0;i<4;i++){const r=e>>>18-6*i<<26>>>26;t+=o.#c[r]}return t}static#h(e){return Math.abs(e).toString(2).length}static get#c(){return"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"}}class l{#b;#w;#p;#z;constructor(e){const{luma:i,chromaBlue:r,chromaRed:a}=e.match(/^(?<luma>[0-7]):(?<chromaBlue>[0-7]):(?<chromaRed>[0-7])$/)?.groups||{};if(!i||!r||!a)throw new t({name:"SubsamplingError",message:'Must be of form "x:y:z", digits from 0-7 inclusive',"subsampling-string":e});this.#b=parseInt(i),this.#w=parseInt(r),this.#p=parseInt(a),this.#z=this.#b+this.#w+this.#p}key(e){const t=e%this.#z;return t<this.#b?"luma":t<this.#b+this.#w?"chromaBlue":"chromaRed"}get luma(){return this.#b}get chromaBlue(){return this.#w}get chromaRed(){return this.#p}}function*d(e,t){const i=Array.from(Array.from({length:e}),(()=>Array.from({length:t}).fill(0)));let[r,a]=[0,0];for(;;)if(i[r][a]=yield i,0===a||r===e-1){const i=r+a+1;if(i>e+t-1)break;a=r>=t-1?t-1:r+1,r=i-a}else a--,r++}const f=(e,t=0,i=255,r=!0)=>(r&&(e=Math.round(e),t=Math.ceil(t),i=Math.floor(i)),Math.max(t,Math.min(i,e))),g=(e,t,i)=>{const r=[];for(let a=0;a<i.sample.width;a++){const i=e[a];r.push(m(i,t.y))}return m(r,t.x)},u=e=>{for(;e>=2*Math.PI;)e-=2*Math.PI;return e>Math.PI&&(e=2*Math.PI-e),e>Math.PI/2?5*(e=(e-Math.PI)*(e-Math.PI))/(e+Math.PI*Math.PI)-1:1-5*(e*=e)/(e+Math.PI*Math.PI)},m=(e,t)=>{let i=0;for(let r=0;r<e.length;r++){let a=e[r]*u((2*t+1)*Math.PI*r/(2*e.length));a*=0===r?Math.SQRT1_2:1,i+=a}return i*=Math.sqrt(2/e.length),i=Math.round(i),i},c=0,b=48,w=57,p=8;class z{#P;#i;#R;#M;constructor(e){if(!e.match(/^[A-Za-z0-9\-_]{16,}$/))throw new t({name:"BlurridDecoderError",message:"String minimum 16 url-safe base 64 encoded chars","serialized-dct":e});this.#P=e,this.#i=o.from(e),this.#R=new h(this.#S()),this.#M=this.#y()}toImageData(){const{luma:e,chromaBlue:i,chromaRed:r}=this.#M;return((e,t=255)=>{const i=[];for(let n=0;n<e.length;n+=3){const{red:h,green:o,blue:l}=(r=e[n],a=e[n+1],s=e[n+2],r=f(r),a=f(a),s=f(s),a-=128,{red:f(r+1.4*(s-=128)),green:f(r-.34*a-.71*s),blue:f(r+1.77*a)});i.push(h,o,l,t)}var r,a,s;return i})((e=>{const i=e.map((e=>n(e))),r=i[0].length,a=Array.from({length:i.length*r}).fill(0);for(const[e,s]of i.entries()){if(s.length!==r)throw new t({name:"ChannelError",message:"Sample channels not of same length","default-length":r,"channel-length":s.length});for(const[t,r]of s.entries())a[i.length*t+e]=r}return a})(((e,t)=>{const[i,r,a]=e,s=()=>Array.from(Array.from({length:t.image.height}),(()=>Array.from({length:t.image.width}).fill(0))),n=[s(),s(),s()],[h,o,l]=n;for(let e=0;e<t.image.height;e++)for(let s=0;s<t.image.width;s++){const n={x:s/t.image.width*t.sample.width,y:e/t.image.height*t.sample.height};h[e][s]=g(i,n,t),o[e][s]=g(r,n,t),l[e][s]=g(a,n,t)}return n})([e,i,r],this.#R)))}#S(){this.#i.readPointer=c;return{image:{width:this.#i.read(16),height:this.#i.read(16)},sample:{width:this.#i.read(8),height:this.#i.read(8)}}}#B(){this.#i.readPointer=b;const e=this.#i.read(3),t=this.#i.read(3),i=this.#i.read(3);return new l(`${e}:${t}:${i}`)}#y(){const e=this.#v();let[t,i,r]=e.next().value;for(this.#i.readPointer=w,this.#i.lastReadSize=p;;){const a=this.#i.readRelative({signed:!0});if(isNaN(a))break;[t,i,r]=e.next(a).value}return{luma:t,chromaBlue:i,chromaRed:r}}#v(){return function*(e,t,i){const r={luma:d(e,t),chromaBlue:d(e,t),chromaRed:d(e,t)},a=[r.luma.next().value,r.chromaBlue.next().value,r.chromaRed.next().value];let s=0;do{const e=yield a,t=i.key(s),n=r[t].next(e);if(n.done)break;"luma"===t?a[0]=n.value:"chromaBlue"===t?a[1]=n.value:a[2]=n.value}while(++s)}(this.#R.sample.width,this.#R.sample.height,this.#B())}get serializedDct(){return this.#P}get metadata(){return this.#R}get coefficients(){return this.#M}}function*P(e){const[i,r]=[e[0].length,e.length];for(const r of e)if(r.length!==i)throw new t({name:"DctError",message:"Coefficient row width does not match image width","image-width":length,"row-width":r.length});let[a,s]=[0,0];for(;;)if(yield e[s][a],0===s||a===i-1){const e=a+s+1;if(e>i+r-2)break;s=a>=r-1?r-1:a+1,a=e-s}else s--,a++}const R=e=>{const t=[];for(let i=0;i<e.length;i++)t.push(M(e[i]));const i=Array.from(Array.from({length:t.length}),(()=>[]));for(let e=0;e<t[0].length;e++){const r=[];for(let i=0;i<t.length;i++)r.push(t[i][e]);for(const[e,t]of M(r).entries())i[e].push(t)}return i},M=e=>{const t=[];for(let i=0;i<e.length;i++){let r=0;for(let t=0;t<e.length;t++)r+=e[t]*Math.cos((2*t+1)*Math.PI*i/(2*e.length));r*=Math.sqrt(2/e.length),r*=0===i?Math.sqrt(.5):1,t.push(Math.round(r))}return t};class S{#R;#i;constructor({buffer:e,width:t,height:i}){const r=Math.min(t,i)/Math.max(t,i),a=Math.round(Math.sqrt(e.length/(4*r))),s=e.length/4/a,{sampleWidth:n,sampleHeight:o}=t>i?{sampleWidth:a,sampleHeight:s}:{sampleWidth:s,sampleHeight:a},l={width:t,height:i},d={width:n,height:o};this.#R=new h({image:l,sample:d}),this.#i=e}toString({length:e=64,subsampling:t="4:2:2"}={}){const i=new o({length:e}),r=new l(t);return this.#L(i),this.#A(i,r),this.#C(i,r),i.toString()}#L(e){e.writePointer=c,e.write(this.#R.image.width,{size:16}),e.write(this.#R.image.height,{size:16}),e.write(this.#R.sample.width,{size:8}),e.write(this.#R.sample.height,{size:8})}#A(e,t){e.writePointer=b,e.write(t.luma,{size:3}),e.write(t.chromaBlue,{size:3}),e.write(t.chromaRed,{size:3})}#C(e,t){e.writePointer=w,e.lastWriteSize=p;for(const i of this.#I(t)){const t=e.writeRelative(i,{signed:!0});if(isNaN(t))break}}#I(e){return function*(e,t){const[i,r,a]=e,s={luma:P(i),chromaBlue:P(r),chromaRed:P(a)};let n=0;do{const e=s[t.key(n)].next();if(e.done)break;yield e.value}while(++n)}((e=>e.map(R))(((e,i,r=3)=>{if(e.length%r)throw new t({name:"ChannelError",message:"Data length is not a multiple of requested channel count","data-length":e.length,"channels-requested":r});const a=Array.from(Array.from({length:r}),(()=>[]));for(const[t,i]of e.entries())a[t%r].push(i);return a.map((e=>s(e,i)))})((e=>{const t=[];for(let s=0;s<e.length;s+=4){const{luma:n,chromaBlue:h,chromaRed:o}=(i=e[s],r=e[s+1],a=e[s+2],i=f(i),r=f(r),a=f(a),{luma:f(.3*i+.59*r+.11*a),chromaBlue:f(128-.17*i-.33*r+.5*a),chromaRed:f(128+.5*i-.42*r-.08*a)});t.push(n,h,o)}var i,r,a;return t})(this.#i),this.#R.sample.width)),e)}}const y=async(t,i=16)=>{const r=e(t),a=await r.ensureAlpha().resize(i,i,{fit:"inside"}).raw().toBuffer(),s=await r.metadata(),{width:n,height:h}={width:0,height:0,...s};return{buffer:a,width:n,height:h}},B=(t,i)=>{const r=Buffer.from(t.toImageData()),{width:a,height:s}=t.metadata.image;return e(r,{raw:{width:a,height:s,channels:4}}).toFile(i)};export{z as BlurridDecoder,S as BlurridEncoder,y as loadImage,B as saveImage};