UNPKG

@blameitonyourisp/blurrid

Version:

Generate and render blurred placeholders for lazy loaded images.

2 lines (1 loc) 10.6 kB
class e extends Error{constructor(e){super();let t="";for(const r in e)switch(r){case"name":this.name=e[r];break;case"message":t=`${e[r]}\n${t}`;break;default:{const a=`${r}: ${e[r]}`;t=`${t}${i(a,"gray",1)}\n`}}this.message=t||""}}const t=(e,{modifiers:t=[],tabs:i=0,tabSize:a=4}={})=>{let s="";for(const e of t)s=`${s}${e}`;const n=r.decorations.reset;return`${s}${" ".repeat(i*a)}${e}${n}`},i=(e,i,a=0)=>{const s=r.fgColors[i];return t(e,s?{modifiers:[s],tabs:a}:{tabs:a})},r={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:""}},a=t=>{const i=t[0].length,r=[];for(const a of t){if(a.length!==i)throw new e({name:"ChannelError",message:"Channel row width does not match sample width","sample-width":length,"row-width":a.length});r.push(...a)}return r};class s{#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 n{#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=n.#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:n.#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=n.#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:n.#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:t,targetStart:i=t?.writePointer||0,sourceStart:r=0,sourceEnd:a=this.bitLength}={}){if(r<0||a>this.bitLength)throw new e({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,h=Math.ceil((s+i)/8);t??=new n({size:h});const o=t.bitLength-i;if(s>o)throw new e({name:"BitBufferError",message:"Source bits exceed bits available in target buffer","source-bits":s,"target-bits":o});for(let e=0;e<s;e++)t.write(this.#f(1,{offset:r+e}),{size:1,offset:i+e});return t}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+=n.#u(i),i=0)}return e}#o(){const e={writeable:!0,offset:this.#s},t=(i,{size:r=n.#h(i),offset:a=e.offset,signed:s=!1}={})=>{if(e.writeable){const t=Math.abs(i),h=this.bitLength-a;e.writeable=!(!s&&i<0)&&(!(n.#h(t)>r)&&(!!Number.isInteger(t)&&(!(r<0||r>32)&&!(r+(s?1:0)>h))))}return e.offset+=r,{append:t,get isWriteable(){return e.writeable}}};return{append:t,get isWriteable(){return e.writeable}}}#l(e,{size:t=n.#h(e),offset:i=this.#s,signed:r=!1}={}){const a=Math.abs(e),{view:s,byteLength:h,subBit:o}=this.#g(t,i);for(let e=0;e<h;e++){const i=s.getUint8(e);let r=0;for(let s=0;s<8;s++){const n=8*e+s;r|=(n<o||n>o+t?i<<24+s>>>31:a<<32-t+(n-o)>>>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.#g(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)}#g(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(t){if(!t.match(/^[A-Za-z0-9\-_]*$/))throw new e({name:"BitBufferError",message:"Encoded string is not url-safe base 64 encoded","encoded-string":t});const i=new n({size:Math.ceil(3*t.length/4)}),r=/[A-Za-z0-9\-_]{1,4}/g;for(const e of t.match(r)||[]){const t=n.#c(e.padEnd(4,"A"));i.write(t,{size:24})}return i.writePointer=0,i.readPointer=0,i}static#c(e){let t=0;for(const[i,r]of e.split("").entries()){t=(t|n.#m.indexOf(r)<<18-6*i)>>>0}return t}static#u(e){let t="";for(let i=0;i<4;i++){const r=e>>>18-6*i<<26>>>26;t+=n.#m[r]}return t}static#h(e){return Math.abs(e).toString(2).length}static get#m(){return"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"}}class h{#b;#w;#p;#z;constructor(t){const{luma:i,chromaBlue:r,chromaRed:a}=t.match(/^(?<luma>[0-7]):(?<chromaBlue>[0-7]):(?<chromaRed>[0-7])$/)?.groups||{};if(!i||!r||!a)throw new e({name:"SubsamplingError",message:'Must be of form "x:y:z", digits from 0-7 inclusive',"subsampling-string":t});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*o(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 l=(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))),d=(e,t,i)=>{const r=[];for(let a=0;a<i.sample.width;a++){const i=e[a];r.push(u(i,t.y))}return u(r,t.x)},f=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)},u=(e,t)=>{let i=0;for(let r=0;r<e.length;r++){let a=e[r]*f((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},g=0,c=48,m=57,b=8;class w{#P;#i;#R;#S;constructor(t){if(!t.match(/^[A-Za-z0-9\-_]{16,}$/))throw new e({name:"BlurridDecoderError",message:"String minimum 16 url-safe base 64 encoded chars","serialized-dct":t});this.#P=t,this.#i=n.from(t),this.#R=new s(this.#M()),this.#S=this.#y()}toImageData(){const{luma:t,chromaBlue:i,chromaRed:r}=this.#S;return((e,t=255)=>{const i=[];for(let n=0;n<e.length;n+=3){const{red:h,green:o,blue:d}=(r=e[n],a=e[n+1],s=e[n+2],r=l(r),a=l(a),s=l(s),a-=128,{red:l(r+1.4*(s-=128)),green:l(r-.34*a-.71*s),blue:l(r+1.77*a)});i.push(h,o,d,t)}var r,a,s;return i})((t=>{const i=t.map((e=>a(e))),r=i[0].length,s=Array.from({length:i.length*r}).fill(0);for(const[t,a]of i.entries()){if(a.length!==r)throw new e({name:"ChannelError",message:"Sample channels not of same length","default-length":r,"channel-length":a.length});for(const[e,r]of a.entries())s[i.length*e+t]=r}return s})(((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]=d(i,n,t),o[e][s]=d(r,n,t),l[e][s]=d(a,n,t)}return n})([t,i,r],this.#R)))}#M(){this.#i.readPointer=g;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=c;const e=this.#i.read(3),t=this.#i.read(3),i=this.#i.read(3);return new h(`${e}:${t}:${i}`)}#y(){const e=this.#v();let[t,i,r]=e.next().value;for(this.#i.readPointer=m,this.#i.lastReadSize=b;;){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:o(e,t),chromaBlue:o(e,t),chromaRed:o(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.#S}}onmessage=e=>{const t=new w(e.data.serializedDct);t.metadata.resize(e.data.width);const i=new ImageData(new Uint8ClampedArray(t.toImageData()),t.metadata.image.width,t.metadata.image.height);postMessage({imageData:i})};