Skip to content

Instantly share code, notes, and snippets.

@tscholl2
Last active August 19, 2020 14:38
Show Gist options
  • Select an option

  • Save tscholl2/bb7a03f71a7e22d2686e1039f285d9a7 to your computer and use it in GitHub Desktop.

Select an option

Save tscholl2/bb7a03f71a7e22d2686e1039f285d9a7 to your computer and use it in GitHub Desktop.

Revisions

  1. tscholl2 revised this gist Aug 19, 2020. 2 changed files with 23 additions and 12 deletions.
    33 changes: 22 additions & 11 deletions png.js
    Original file line number Diff line number Diff line change
    @@ -1,18 +1,29 @@
    /**
    * Encode takes a string as input, encodes it as UTF8
    * then embeds it into a PNG which has builtin compression.
    * @param {string} input
    * @returns {Promise<string>}
    */
    async function encode(input) {
    // TODO: handle unicode?
    const bytes = new TextEncoder("utf-8").encode(input);
    const canvas = document.createElement("canvas");
    canvas.width = Math.ceil(Math.sqrt(input.length / 3));
    canvas.width = Math.ceil(Math.sqrt(bytes.length / 3 + 2));
    canvas.height = canvas.width;
    const ctx = canvas.getContext('2d');
    const imgData = ctx.createImageData(canvas.width, canvas.height);
    for (let i = 0, j = 0; i < 7; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (input.length >>> (8 * j++)) & 0xff;
    for (let i = 7, j = 0; i < 4 * canvas.width * canvas.height; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : input.charCodeAt(j++) || 0;
    for (let i = 0, j = 0; j < 4; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (bytes.length >> (8 * j++)) & 0xff;
    for (let i = 5, j = 0; i < 4 * canvas.width * canvas.height; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : bytes[j++] || 0;
    ctx.putImageData(imgData, 0, 0);
    return canvas.toDataURL('image/png', 0);
    }

    /**
    * Reverse of encode.
    * @param {string} input
    * @returns {Promise<string>}
    */
    async function decode(input) {
    const image = new Image();
    image.src = input;
    @@ -24,10 +35,10 @@ async function decode(input) {
    ctx.drawImage(image, 0, 0);
    const imgData = ctx.getImageData(0, 0, image.width, image.height);
    let length = 0;
    for (let i = 0, j = 0; i < 7; i++)
    for (let i = 0, j = 0; j < 4; i++)
    length |= i % 4 == 3 ? 0 : (imgData.data[i] << (8 * j++));
    let output = "";
    for (let i = 7; i < imgData.data.length; i++)
    output += i % 4 == 3 ? "" : String.fromCharCode(imgData.data[i]);
    return output.substr(0, length);
    const output = imgData.data
    .filter((_, i) => i > 4 && i % 4 != 3)
    .slice(0, length)
    return new TextDecoder("utf-8").decode(output);
    }
    2 changes: 1 addition & 1 deletion png.min.js
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    (()=>{return{encode:async function(t){const e=document.createElement("canvas");e.width=Math.ceil(Math.sqrt(t.length/3)),e.height=e.width;const a=e.getContext("2d"),n=a.createImageData(e.width,e.height);for(let e=0,a=0;e<7;e++)n.data[e]=e%4==3?255:t.length>>>8*a++&255;for(let a=7,h=0;a<4*e.width*e.height;a++)n.data[a]=a%4==3?255:t.charCodeAt(h++)||0;return a.putImageData(n,0,0),e.toDataURL("image/png",0)},decode:async function(t){const e=new Image;e.src=t,await new Promise(t=>e.onload=t);const a=document.createElement("canvas");a.width=e.width,a.height=e.height;const n=a.getContext("2d");n.drawImage(e,0,0);const h=n.getImageData(0,0,e.width,e.height);let o=0;for(let t=0,e=0;t<7;t++)o|=t%4==3?0:h.data[t]<<8*e++;let d="";for(let t=7;t<h.data.length;t++)d+=t%4==3?"":String.fromCharCode(h.data[t]);return d.substr(0,o)}}});
    (()=>{return{encode:async function(t){const e=new TextEncoder("utf-8").encode(t),a=document.createElement("canvas");a.width=Math.ceil(e.length/3+2),a.height=1;const n=a.getContext("2d"),c=n.createImageData(a.width,1);for(let t=0,a=0;a<4;t++)c.data[t]=t%4==3?255:e.length>>8*a++&255;for(let t=5,n=0;t<4*a.width;t++)c.data[t]=t%4==3?255:e[n++]||0;return n.putImageData(c,0,0),a.toDataURL("image/png",0)},decode:async function(t){const e=new Image;e.src=t,await new Promise(t=>e.onload=t);const a=document.createElement("canvas");a.width=e.width,a.height=1;const n=a.getContext("2d");n.drawImage(e,0,0);const c=n.getImageData(0,0,e.width,1);let o=0;for(let t=0,e=0;e<4;t++)o|=t%4==3?0:c.data[t]<<8*e++;const d=c.data.filter((t,e)=>e>4&&e%4!=3).slice(0,o);return new TextDecoder("utf-8").decode(d)}}})();
  2. tscholl2 revised this gist Aug 19, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion LZW.min.js
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    (()=>{const t=String.fromCodePoint,e="length";return{compress:function(o){const r=[],n=new Map;for(let e=0;e<256;e++)n.set(t(e),e);let s="",c="",l="";for(let e of o)l=s+(c=t(e)),n.has(l)?s=l:(r.push(n.set(l,n.size).get(s)),s=c);return r.push(n.get(s)),(t=>{const o=t.reduce((t=0,e)=>e>t?e:t),r=1+Math.floor(Math.log2(o)),n=new Uint8Array(5+Math.ceil(t[e]*r/8));n[0]=r;for(let o=0;o<4;o++)n[1+o]=t[e]>>8*o&255;for(let o=0;o<t[e];o++)for(let e=0;e<r;e++){const s=40+(o*r+e);n[s>>3]|=(1<<s%8)*(t[o]>>e&1)}return n})(r)},decompress:function(o){const r=(t=>{const o=t[0],r=t.slice(1,5).reverse().reduce((t,e)=>t<<8|e,0),n=new Uint32Array(r);for(let r=0;r<t[e];r++)for(let e=0;e<o;e++){const s=40+(r*o+e);n[r]|=(1<<e)*(t[s>>3]>>s%8&1)}return n})(o),n=[];for(let e=0;e<256;e++)n.push(t(e));let s=n[r[0]],c="";const l=[s];for(let t of r.slice(1))l.push(c=t<n[e]?n[t]:s+s[0]),n.push(s+c[0]),s=c;const f=[];for(let t of l.join(""))f.push(t.codePointAt(0));return new Uint8Array(f)}}})();
    (()=>{const t=String.fromCodePoint,e="length";return{compress:(o)=>{const r=[],n=new Map;for(let e=0;e<256;e++)n.set(t(e),e);let s="",c="",l="";for(let e of o)l=s+(c=t(e)),n.has(l)?s=l:(r.push(n.set(l,n.size).get(s)),s=c);return r.push(n.get(s)),(t=>{const o=t.reduce((t=0,e)=>e>t?e:t),r=1+Math.floor(Math.log2(o)),n=new Uint8Array(5+Math.ceil(t[e]*r/8));n[0]=r;for(let o=0;o<4;o++)n[1+o]=t[e]>>8*o&255;for(let o=0;o<t[e];o++)for(let e=0;e<r;e++){const s=40+(o*r+e);n[s>>3]|=(1<<s%8)*(t[o]>>e&1)}return n})(r)},decompress:(o)=>{const r=(t=>{const o=t[0],r=t.slice(1,5).reverse().reduce((t,e)=>t<<8|e,0),n=new Uint32Array(r);for(let r=0;r<t[e];r++)for(let e=0;e<o;e++){const s=40+(r*o+e);n[r]|=(1<<e)*(t[s>>3]>>s%8&1)}return n})(o),n=[];for(let e=0;e<256;e++)n.push(t(e));let s=n[r[0]],c="";const l=[s];for(let t of r.slice(1))l.push(c=t<n[e]?n[t]:s+s[0]),n.push(s+c[0]),s=c;const f=[];for(let t of l.join(""))f.push(t.codePointAt(0));return new Uint8Array(f)}}})();
  3. tscholl2 revised this gist Aug 10, 2020. 4 changed files with 3 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion LZW.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    (function () {
    (() => {
    const fcp = String.fromCodePoint;
    const L = "length";
    /**
    2 changes: 1 addition & 1 deletion LZW.min.js
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    (function(){const t=String.fromCodePoint,e="length";return{compress:function(o){const r=[],n=new Map;for(let e=0;e<256;e++)n.set(t(e),e);let s="",c="",f="";for(let e of o)f=s+(c=t(e)),n.has(f)?s=f:(r.push(n.set(f,n.size).get(s)),s=c);return r.push(n.get(s)),(t=>{const o=t.reduce((t=0,e)=>e>t?e:t),r=1+Math.floor(Math.log2(o)),n=new Uint8Array(5+Math.ceil(t[e]*r/8));n[0]=r;for(let o=0;o<4;o++)n[1+o]=t[e]>>8*o&255;for(let o=0;o<t[e];o++)for(let e=0;e<r;e++){const s=40+(o*r+e);n[s>>3]|=(1<<s%8)*(t[o]>>e&1)}return n})(r)},decompress:function(o){const r=(t=>{const o=t[0],r=t.slice(1,5).reverse().reduce((t,e)=>t<<8|e,0),n=new Uint32Array(r);for(let r=0;r<t[e];r++)for(let e=0;e<o;e++){const s=40+(r*o+e);n[r]|=(1<<e)*(t[s>>3]>>s%8&1)}return n})(o),n=[];for(let e=0;e<256;e++)n.push(t(e));let s=n[r[0]],c="";const f=[s];for(let t of r.slice(1))f.push(c=t<n[e]?n[t]:s+s[0]),n.push(s+c[0]),s=c;const l=[];for(let t of f.join(""))l.push(t.codePointAt(0));return new Uint8Array(l)}}})();
    (()=>{const t=String.fromCodePoint,e="length";return{compress:function(o){const r=[],n=new Map;for(let e=0;e<256;e++)n.set(t(e),e);let s="",c="",l="";for(let e of o)l=s+(c=t(e)),n.has(l)?s=l:(r.push(n.set(l,n.size).get(s)),s=c);return r.push(n.get(s)),(t=>{const o=t.reduce((t=0,e)=>e>t?e:t),r=1+Math.floor(Math.log2(o)),n=new Uint8Array(5+Math.ceil(t[e]*r/8));n[0]=r;for(let o=0;o<4;o++)n[1+o]=t[e]>>8*o&255;for(let o=0;o<t[e];o++)for(let e=0;e<r;e++){const s=40+(o*r+e);n[s>>3]|=(1<<s%8)*(t[o]>>e&1)}return n})(r)},decompress:function(o){const r=(t=>{const o=t[0],r=t.slice(1,5).reverse().reduce((t,e)=>t<<8|e,0),n=new Uint32Array(r);for(let r=0;r<t[e];r++)for(let e=0;e<o;e++){const s=40+(r*o+e);n[r]|=(1<<e)*(t[s>>3]>>s%8&1)}return n})(o),n=[];for(let e=0;e<256;e++)n.push(t(e));let s=n[r[0]],c="";const l=[s];for(let t of r.slice(1))l.push(c=t<n[e]?n[t]:s+s[0]),n.push(s+c[0]),s=c;const f=[];for(let t of l.join(""))f.push(t.codePointAt(0));return new Uint8Array(f)}}})();
    File renamed without changes.
    1 change: 1 addition & 0 deletions png.min.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    (()=>{return{encode:async function(t){const e=document.createElement("canvas");e.width=Math.ceil(Math.sqrt(t.length/3)),e.height=e.width;const a=e.getContext("2d"),n=a.createImageData(e.width,e.height);for(let e=0,a=0;e<7;e++)n.data[e]=e%4==3?255:t.length>>>8*a++&255;for(let a=7,h=0;a<4*e.width*e.height;a++)n.data[a]=a%4==3?255:t.charCodeAt(h++)||0;return a.putImageData(n,0,0),e.toDataURL("image/png",0)},decode:async function(t){const e=new Image;e.src=t,await new Promise(t=>e.onload=t);const a=document.createElement("canvas");a.width=e.width,a.height=e.height;const n=a.getContext("2d");n.drawImage(e,0,0);const h=n.getImageData(0,0,e.width,e.height);let o=0;for(let t=0,e=0;t<7;t++)o|=t%4==3?0:h.data[t]<<8*e++;let d="";for(let t=7;t<h.data.length;t++)d+=t%4==3?"":String.fromCharCode(h.data[t]);return d.substr(0,o)}}});
  4. tscholl2 revised this gist Aug 10, 2020. 2 changed files with 83 additions and 0 deletions.
    82 changes: 82 additions & 0 deletions LZW.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    (function () {
    const fcp = String.fromCodePoint;
    const L = "length";
    /**
    * @param {Uint8Array} input
    * @returns {Uint8Array}
    */
    function compress(input) {
    const codewords = [];
    const dictionary = new Map();
    for (let i = 0; i < 256; i++)
    dictionary.set(fcp(i), i);
    let buf = "", next = "", bufnext = "";
    for (let x of input) {
    next = fcp(x);
    bufnext = buf + next;
    if (dictionary.has(bufnext))
    buf = bufnext;
    else {
    codewords.push(dictionary.set(bufnext, dictionary.size).get(buf));
    buf = next;
    }
    }
    codewords.push(dictionary.get(buf));
    return ((input) => {
    /**
    * Condenses a byte array into an array of numbers.
    * @param {Uint32Array} input
    * @returns {Uint8Array}
    */
    const max = input.reduce((p = 0, n) => n > p ? n : p);
    const bits = 1 + Math.floor(Math.log2(max));
    const output = new Uint8Array(1 + 4 + Math.ceil(input[L] * bits / 8));
    output[0] = bits;
    for (let i = 0; i < 4; i++)
    output[1 + i] = (input[L] >> (i * 8)) & 0xff;
    for (let i = 0; i < input[L]; i++)
    for (let j = 0; j < bits; j++) {
    const k = 8 * 5 + (i * bits + j);
    output[k >> 3] |= (1 << ((k % 8))) * ((input[i] >> j) & 1);
    }
    return output;
    })(codewords);
    }
    /**
    * @param {Uint8Array} input
    * @returns {Uint8Array}
    */
    function decompress(input) {
    const codewords = ((input) => {
    /**
    * Uncondenses a byte array into an array of numbers.
    * @param {Uint8Array} input
    * @returns {Uint32Array}
    */
    const bits = input[0];
    const length = input.slice(1, 5).reverse().reduce((p, n) => (p << 8) | n, 0);
    const output = new Uint32Array(length);
    for (let i = 0; i < input[L]; i++)
    for (let j = 0; j < bits; j++) {
    const k = 8 * 5 + (i * bits + j);
    output[i] |= (1 << j) * ((input[k >> 3] >> (k % 8)) & 1);
    }
    return output;
    })(input);
    const dictionary = [];
    for (let i = 0; i < 256; i++)
    dictionary.push(fcp(i));
    let prev = dictionary[codewords[0]], next = "";
    const values = [prev];
    for (let k of codewords.slice(1)) {
    values.push(next = k < dictionary[L] ? dictionary[k] : prev + prev[0]);
    dictionary.push(prev + next[0]);
    prev = next;
    }
    const output = [];
    for (let c of values.join(""))
    output.push(c.codePointAt(0));
    return new Uint8Array(output);
    }
    return { compress, decompress };
    })()
    1 change: 1 addition & 0 deletions LZW.min.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    (function(){const t=String.fromCodePoint,e="length";return{compress:function(o){const r=[],n=new Map;for(let e=0;e<256;e++)n.set(t(e),e);let s="",c="",f="";for(let e of o)f=s+(c=t(e)),n.has(f)?s=f:(r.push(n.set(f,n.size).get(s)),s=c);return r.push(n.get(s)),(t=>{const o=t.reduce((t=0,e)=>e>t?e:t),r=1+Math.floor(Math.log2(o)),n=new Uint8Array(5+Math.ceil(t[e]*r/8));n[0]=r;for(let o=0;o<4;o++)n[1+o]=t[e]>>8*o&255;for(let o=0;o<t[e];o++)for(let e=0;e<r;e++){const s=40+(o*r+e);n[s>>3]|=(1<<s%8)*(t[o]>>e&1)}return n})(r)},decompress:function(o){const r=(t=>{const o=t[0],r=t.slice(1,5).reverse().reduce((t,e)=>t<<8|e,0),n=new Uint32Array(r);for(let r=0;r<t[e];r++)for(let e=0;e<o;e++){const s=40+(r*o+e);n[r]|=(1<<e)*(t[s>>3]>>s%8&1)}return n})(o),n=[];for(let e=0;e<256;e++)n.push(t(e));let s=n[r[0]],c="";const f=[s];for(let t of r.slice(1))f.push(c=t<n[e]?n[t]:s+s[0]),n.push(s+c[0]),s=c;const l=[];for(let t of f.join(""))l.push(t.codePointAt(0));return new Uint8Array(l)}}})();
  5. tscholl2 revised this gist Aug 5, 2020. 1 changed file with 32 additions and 33 deletions.
    65 changes: 32 additions & 33 deletions png-compression.js
    Original file line number Diff line number Diff line change
    @@ -1,34 +1,33 @@
    async function encode(input) {
    // TODO: handle unicode?
    const canvas = document.createElement("canvas");
    canvas.width = Math.ceil(Math.sqrt(input.length / 3));
    canvas.height = canvas.width;
    const ctx = canvas.getContext('2d');
    const imgData = ctx.createImageData(canvas.width, canvas.height);
    for (let i = 0, j = 0; i < 7; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (input.length >>> (8 * j++)) & 0xff;
    for (let i = 7, j = 0; i < 4 * canvas.width; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : input.charCodeAt(j++) || 0;
    ctx.putImageData(imgData, 0, 0);
    const s = canvas.toDataURL('image/png', 0);
    return s;
    }
    async function encode(input) {
    // TODO: handle unicode?
    const canvas = document.createElement("canvas");
    canvas.width = Math.ceil(Math.sqrt(input.length / 3));
    canvas.height = canvas.width;
    const ctx = canvas.getContext('2d');
    const imgData = ctx.createImageData(canvas.width, canvas.height);
    for (let i = 0, j = 0; i < 7; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (input.length >>> (8 * j++)) & 0xff;
    for (let i = 7, j = 0; i < 4 * canvas.width * canvas.height; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : input.charCodeAt(j++) || 0;
    ctx.putImageData(imgData, 0, 0);
    return canvas.toDataURL('image/png', 0);
    }

    async function decode(input) {
    const image = new Image();
    image.src = input;
    await new Promise(r => image.onload = r);
    const canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    const imgData = ctx.getImageData(0, 0, image.width, image.height);
    let length = 0;
    for (let i = 0, j = 0; i < 7; i++)
    length |= i % 4 == 3 ? 0 : (imgData.data[i] << (8 * j++));
    let output = "";
    for (let i = 7; i < imgData.data.length; i++)
    output += i % 4 == 3 ? "" : String.fromCharCode(imgData.data[i]);
    return output.substr(0, length);
    }
    async function decode(input) {
    const image = new Image();
    image.src = input;
    await new Promise(r => image.onload = r);
    const canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    const imgData = ctx.getImageData(0, 0, image.width, image.height);
    let length = 0;
    for (let i = 0, j = 0; i < 7; i++)
    length |= i % 4 == 3 ? 0 : (imgData.data[i] << (8 * j++));
    let output = "";
    for (let i = 7; i < imgData.data.length; i++)
    output += i % 4 == 3 ? "" : String.fromCharCode(imgData.data[i]);
    return output.substr(0, length);
    }
  6. tscholl2 revised this gist Aug 5, 2020. 1 changed file with 33 additions and 35 deletions.
    68 changes: 33 additions & 35 deletions png-compression.js
    Original file line number Diff line number Diff line change
    @@ -1,36 +1,34 @@
    async function encode(input) {
    // TODO: handle unicode?
    const canvas = document.createElement("canvas");
    canvas.width = Math.ceil(input.length / 3) + 8;
    canvas.height = 1;
    const ctx = canvas.getContext('2d');
    const imgData = ctx.createImageData(canvas.width, 1);
    for (let i = 0, j = 0; i < 7; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (input.length >>> (8 * j++)) & 0xff;
    for (let i = 7, j = 0; i < 4 * canvas.width; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : input.charCodeAt(j++) || 0;
    ctx.putImageData(imgData, 0, 0);
    return canvas.toDataURL('image/png', 0);
    }
    async function encode(input) {
    // TODO: handle unicode?
    const canvas = document.createElement("canvas");
    canvas.width = Math.ceil(Math.sqrt(input.length / 3));
    canvas.height = canvas.width;
    const ctx = canvas.getContext('2d');
    const imgData = ctx.createImageData(canvas.width, canvas.height);
    for (let i = 0, j = 0; i < 7; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (input.length >>> (8 * j++)) & 0xff;
    for (let i = 7, j = 0; i < 4 * canvas.width; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : input.charCodeAt(j++) || 0;
    ctx.putImageData(imgData, 0, 0);
    const s = canvas.toDataURL('image/png', 0);
    return s;
    }

    async function decode(input) {
    console.log(input)
    const image = new Image();
    image.src = input;
    await new Promise(r => image.onload = r);
    const canvas = document.createElement("canvas");
    canvas.width = image.width;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    const imgData = ctx.getImageData(0, 0, image.width, 1);
    let length = 0;
    for (let i = 0, j = 0; i < 7; i++)
    length |= i % 4 == 3 ? 0 : (imgData.data[i] << (8 * j++));
    let output = "";
    for (let i = 7; i < imgData.data.length; i++)
    output += i % 4 == 3 ? "" : String.fromCharCode(imgData.data[i]);
    return output.substr(0, length);
    }

    // Both minified
    async function encode(t){const e=document.createElement("canvas");e.width=Math.ceil(t.length/3)+8,e.height=1;const a=e.getContext("2d"),n=a.createImageData(e.width,1);for(let e=0,a=0;e<7;e++)n.data[e]=e%4==3?255:t.length>>>8*a++&255;for(let a=7,o=0;a<4*e.width;a++)n.data[a]=a%4==3?255:t.charCodeAt(o++)||0;return a.putImageData(n,0,0),e.toDataURL("image/png",0)}async function decode(t){console.log(t);const e=new Image;e.src=t,await new Promise(t=>e.onload=t);const a=document.createElement("canvas");a.width=e.width;const n=a.getContext("2d");n.drawImage(e,0,0);const o=n.getImageData(0,0,e.width,1);let c=0;for(let t=0,e=0;t<7;t++)c|=t%4==3?0:o.data[t]<<8*e++;let d="";for(let t=7;t<o.data.length;t++)d+=t%4==3?"":String.fromCharCode(o.data[t]);return d.substr(0,c)}
    async function decode(input) {
    const image = new Image();
    image.src = input;
    await new Promise(r => image.onload = r);
    const canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    const imgData = ctx.getImageData(0, 0, image.width, image.height);
    let length = 0;
    for (let i = 0, j = 0; i < 7; i++)
    length |= i % 4 == 3 ? 0 : (imgData.data[i] << (8 * j++));
    let output = "";
    for (let i = 7; i < imgData.data.length; i++)
    output += i % 4 == 3 ? "" : String.fromCharCode(imgData.data[i]);
    return output.substr(0, length);
    }
  7. tscholl2 created this gist Aug 5, 2020.
    36 changes: 36 additions & 0 deletions png-compression.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    async function encode(input) {
    // TODO: handle unicode?
    const canvas = document.createElement("canvas");
    canvas.width = Math.ceil(input.length / 3) + 8;
    canvas.height = 1;
    const ctx = canvas.getContext('2d');
    const imgData = ctx.createImageData(canvas.width, 1);
    for (let i = 0, j = 0; i < 7; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : (input.length >>> (8 * j++)) & 0xff;
    for (let i = 7, j = 0; i < 4 * canvas.width; i++)
    imgData.data[i] = i % 4 == 3 ? 255 : input.charCodeAt(j++) || 0;
    ctx.putImageData(imgData, 0, 0);
    return canvas.toDataURL('image/png', 0);
    }

    async function decode(input) {
    console.log(input)
    const image = new Image();
    image.src = input;
    await new Promise(r => image.onload = r);
    const canvas = document.createElement("canvas");
    canvas.width = image.width;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    const imgData = ctx.getImageData(0, 0, image.width, 1);
    let length = 0;
    for (let i = 0, j = 0; i < 7; i++)
    length |= i % 4 == 3 ? 0 : (imgData.data[i] << (8 * j++));
    let output = "";
    for (let i = 7; i < imgData.data.length; i++)
    output += i % 4 == 3 ? "" : String.fromCharCode(imgData.data[i]);
    return output.substr(0, length);
    }

    // Both minified
    async function encode(t){const e=document.createElement("canvas");e.width=Math.ceil(t.length/3)+8,e.height=1;const a=e.getContext("2d"),n=a.createImageData(e.width,1);for(let e=0,a=0;e<7;e++)n.data[e]=e%4==3?255:t.length>>>8*a++&255;for(let a=7,o=0;a<4*e.width;a++)n.data[a]=a%4==3?255:t.charCodeAt(o++)||0;return a.putImageData(n,0,0),e.toDataURL("image/png",0)}async function decode(t){console.log(t);const e=new Image;e.src=t,await new Promise(t=>e.onload=t);const a=document.createElement("canvas");a.width=e.width;const n=a.getContext("2d");n.drawImage(e,0,0);const o=n.getImageData(0,0,e.width,1);let c=0;for(let t=0,e=0;t<7;t++)c|=t%4==3?0:o.data[t]<<8*e++;let d="";for(let t=7;t<o.data.length;t++)d+=t%4==3?"":String.fromCharCode(o.data[t]);return d.substr(0,c)}