next-blurhash-previews 0.0.3-beta54 → 0.0.3-beta57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/generateBlurhash.js +15 -1
- package/components/workerScript.ts +9 -12
- package/imagePreviewBootstrap.js +290 -2
- package/package.json +1 -1
- package/vite.wc.config.ts +1 -1
- package/vite.worker.config.ts +1 -1
    
        package/bin/generateBlurhash.js
    CHANGED
    
    | @@ -1,8 +1,10 @@ | |
| 1 | 
            +
            import fs from "fs";
         | 
| 2 | 
            +
            import path from "path";
         | 
| 3 | 
            +
             | 
| 1 4 | 
             
            import sharp from "sharp";
         | 
| 2 5 | 
             
            import fetch from "node-fetch";
         | 
| 3 6 | 
             
            import { encode, isBlurhashValid } from "blurhash";
         | 
| 4 7 |  | 
| 5 | 
            -
            import path from "path";
         | 
| 6 8 | 
             
            const __dirname = process.cwd();
         | 
| 7 9 |  | 
| 8 10 | 
             
            export async function getSharpImage(imgPath) {
         | 
| @@ -13,6 +15,18 @@ export async function getSharpImage(imgPath) { | |
| 13 15 |  | 
| 14 16 | 
             
                return sharp(buffer);
         | 
| 15 17 | 
             
              } else {
         | 
| 18 | 
            +
                const ext = path.extname(imgPath);
         | 
| 19 | 
            +
                const dir = path.dirname(imgPath);
         | 
| 20 | 
            +
                const basename = path.basename(imgPath, ext);
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                const previewOption = path.join(dir, basename + "-preview", ext);
         | 
| 23 | 
            +
                console.log("Trying preview", previewOption);
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                if (fs.existsSync()) {
         | 
| 26 | 
            +
                  console.log("Preview found");
         | 
| 27 | 
            +
                  return sharp(previewOption);
         | 
| 28 | 
            +
                }
         | 
| 29 | 
            +
             | 
| 16 30 | 
             
                return sharp(imgPath);
         | 
| 17 31 | 
             
              }
         | 
| 18 32 | 
             
            }
         | 
| @@ -5,16 +5,13 @@ addEventListener("message", evt => { | |
| 5 5 |  | 
| 6 6 | 
             
              const { canvas, width, height, blurhash } = evt.data;
         | 
| 7 7 |  | 
| 8 | 
            -
               | 
| 9 | 
            -
               | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                const end = +new Date();
         | 
| 18 | 
            -
                console.log("Done Encoding", blurhash, end - start);
         | 
| 19 | 
            -
              });
         | 
| 8 | 
            +
              console.log("Encoding", blurhash);
         | 
| 9 | 
            +
              const start = +new Date();
         | 
| 10 | 
            +
              const pixels = decode(blurhash, width, height);
         | 
| 11 | 
            +
              const ctx = canvas.getContext("2d");
         | 
| 12 | 
            +
              const imageData = ctx.createImageData(width, height);
         | 
| 13 | 
            +
              imageData.data.set(pixels);
         | 
| 14 | 
            +
              ctx.putImageData(imageData, 0, 0);
         | 
| 15 | 
            +
              const end = +new Date();
         | 
| 16 | 
            +
              console.log("Done Encoding", blurhash, end - start);
         | 
| 20 17 | 
             
            });
         | 
    
        package/imagePreviewBootstrap.js
    CHANGED
    
    | @@ -4,11 +4,299 @@ export const imagePreviewBootstrap = createElement( | |
| 4 4 | 
             
              Fragment,
         | 
| 5 5 | 
             
              {},
         | 
| 6 6 | 
             
              createElement("script", {
         | 
| 7 | 
            -
                dangerouslySetInnerHTML: { __html: `(() => { "use strict"; | 
| 7 | 
            +
                dangerouslySetInnerHTML: { __html: `(() => { "use strict";
         | 
| 8 | 
            +
            class ImageWithPreview extends HTMLElement {
         | 
| 9 | 
            +
              sd;
         | 
| 10 | 
            +
              mo;
         | 
| 11 | 
            +
              static observedAttributes = ["preview"];
         | 
| 12 | 
            +
              #connected = false;
         | 
| 13 | 
            +
              get #imgEl() {
         | 
| 14 | 
            +
                return this.querySelector("img");
         | 
| 15 | 
            +
              }
         | 
| 16 | 
            +
              get #canvasEl() {
         | 
| 17 | 
            +
                return this.querySelector("canvas");
         | 
| 18 | 
            +
              }
         | 
| 19 | 
            +
              constructor() {
         | 
| 20 | 
            +
                super();
         | 
| 21 | 
            +
                this.sd = this.attachShadow({
         | 
| 22 | 
            +
                  mode: "open"
         | 
| 23 | 
            +
                });
         | 
| 24 | 
            +
                this.sd.innerHTML = \`<slot name="preview"></slot>\`;
         | 
| 25 | 
            +
              }
         | 
| 26 | 
            +
              #checkReady = () => {
         | 
| 27 | 
            +
                if (this.#imgEl && this.#canvasEl) {
         | 
| 28 | 
            +
                  this.mo?.disconnect();
         | 
| 29 | 
            +
                  if (this.#imgEl.complete) {
         | 
| 30 | 
            +
                    this.#imgLoad();
         | 
| 31 | 
            +
                  } else {
         | 
| 32 | 
            +
                    this.#updatePreview();
         | 
| 33 | 
            +
                    this.#imgEl.addEventListener("load", this.#imgLoad);
         | 
| 34 | 
            +
                  }
         | 
| 35 | 
            +
                  return 1;
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
              };
         | 
| 38 | 
            +
              connectedCallback() {
         | 
| 39 | 
            +
                this.#connected = true;
         | 
| 40 | 
            +
                if (!this.#checkReady()) {
         | 
| 41 | 
            +
                  this.mo = new MutationObserver(this.#checkReady);
         | 
| 42 | 
            +
                  this.mo.observe(this, {
         | 
| 43 | 
            +
                    subtree: true,
         | 
| 44 | 
            +
                    childList: true,
         | 
| 45 | 
            +
                    attributes: false
         | 
| 46 | 
            +
                  });
         | 
| 47 | 
            +
                }
         | 
| 48 | 
            +
              }
         | 
| 49 | 
            +
              #imgLoad = () => {
         | 
| 50 | 
            +
                this.sd.innerHTML = \`<slot name="image"></slot>\`;
         | 
| 51 | 
            +
              };
         | 
| 52 | 
            +
              attributeChangedCallback(name) {
         | 
| 53 | 
            +
                if (this.#canvasEl && name === "preview") {
         | 
| 54 | 
            +
                  this.#updatePreview();
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
              }
         | 
| 57 | 
            +
              #updatePreview() {
         | 
| 58 | 
            +
                if (!this.#connected || !this.getAttribute("preview")) {
         | 
| 59 | 
            +
                  return;
         | 
| 60 | 
            +
                }
         | 
| 61 | 
            +
                const previewObj = JSON.parse(this.getAttribute("preview"));
         | 
| 62 | 
            +
                updateBlurHashPreview(this.#canvasEl, previewObj);
         | 
| 63 | 
            +
              }
         | 
| 64 | 
            +
            }
         | 
| 65 | 
            +
            if (!customElements.get("blurhash-image")) {
         | 
| 66 | 
            +
              customElements.define("blurhash-image", ImageWithPreview);
         | 
| 67 | 
            +
            }
         | 
| 68 | 
            +
            function updateBlurHashPreview(canvasEl, preview) {
         | 
| 69 | 
            +
              const {
         | 
| 70 | 
            +
                w: width,
         | 
| 71 | 
            +
                h: height,
         | 
| 72 | 
            +
                blurhash
         | 
| 73 | 
            +
              } = preview;
         | 
| 74 | 
            +
              canvasEl.width = width;
         | 
| 75 | 
            +
              canvasEl.height = height;
         | 
| 76 | 
            +
              {
         | 
| 77 | 
            +
                const workerBlob = new Blob([document.querySelector("#next-blurhash-worker-script").textContent], {
         | 
| 78 | 
            +
                  type: "text/javascript"
         | 
| 79 | 
            +
                });
         | 
| 80 | 
            +
                const worker = new Worker(window.URL.createObjectURL(workerBlob));
         | 
| 81 | 
            +
                const offscreen = canvasEl.transferControlToOffscreen();
         | 
| 82 | 
            +
                worker.postMessage({
         | 
| 83 | 
            +
                  canvas: offscreen,
         | 
| 84 | 
            +
                  width,
         | 
| 85 | 
            +
                  height,
         | 
| 86 | 
            +
                  blurhash
         | 
| 87 | 
            +
                }, [offscreen]);
         | 
| 88 | 
            +
              }
         | 
| 89 | 
            +
            } })();` },
         | 
| 8 90 | 
             
              }),
         | 
| 9 91 | 
             
              createElement("script", {
         | 
| 10 92 | 
             
                id: "next-blurhash-worker-script",
         | 
| 11 93 | 
             
                type: "javascript/worker",
         | 
| 12 | 
            -
                dangerouslySetInnerHTML: { __html: `(() => { "use strict"; | 
| 94 | 
            +
                dangerouslySetInnerHTML: { __html: `(() => { "use strict";
         | 
| 95 | 
            +
            const digitCharacters = [
         | 
| 96 | 
            +
              "0",
         | 
| 97 | 
            +
              "1",
         | 
| 98 | 
            +
              "2",
         | 
| 99 | 
            +
              "3",
         | 
| 100 | 
            +
              "4",
         | 
| 101 | 
            +
              "5",
         | 
| 102 | 
            +
              "6",
         | 
| 103 | 
            +
              "7",
         | 
| 104 | 
            +
              "8",
         | 
| 105 | 
            +
              "9",
         | 
| 106 | 
            +
              "A",
         | 
| 107 | 
            +
              "B",
         | 
| 108 | 
            +
              "C",
         | 
| 109 | 
            +
              "D",
         | 
| 110 | 
            +
              "E",
         | 
| 111 | 
            +
              "F",
         | 
| 112 | 
            +
              "G",
         | 
| 113 | 
            +
              "H",
         | 
| 114 | 
            +
              "I",
         | 
| 115 | 
            +
              "J",
         | 
| 116 | 
            +
              "K",
         | 
| 117 | 
            +
              "L",
         | 
| 118 | 
            +
              "M",
         | 
| 119 | 
            +
              "N",
         | 
| 120 | 
            +
              "O",
         | 
| 121 | 
            +
              "P",
         | 
| 122 | 
            +
              "Q",
         | 
| 123 | 
            +
              "R",
         | 
| 124 | 
            +
              "S",
         | 
| 125 | 
            +
              "T",
         | 
| 126 | 
            +
              "U",
         | 
| 127 | 
            +
              "V",
         | 
| 128 | 
            +
              "W",
         | 
| 129 | 
            +
              "X",
         | 
| 130 | 
            +
              "Y",
         | 
| 131 | 
            +
              "Z",
         | 
| 132 | 
            +
              "a",
         | 
| 133 | 
            +
              "b",
         | 
| 134 | 
            +
              "c",
         | 
| 135 | 
            +
              "d",
         | 
| 136 | 
            +
              "e",
         | 
| 137 | 
            +
              "f",
         | 
| 138 | 
            +
              "g",
         | 
| 139 | 
            +
              "h",
         | 
| 140 | 
            +
              "i",
         | 
| 141 | 
            +
              "j",
         | 
| 142 | 
            +
              "k",
         | 
| 143 | 
            +
              "l",
         | 
| 144 | 
            +
              "m",
         | 
| 145 | 
            +
              "n",
         | 
| 146 | 
            +
              "o",
         | 
| 147 | 
            +
              "p",
         | 
| 148 | 
            +
              "q",
         | 
| 149 | 
            +
              "r",
         | 
| 150 | 
            +
              "s",
         | 
| 151 | 
            +
              "t",
         | 
| 152 | 
            +
              "u",
         | 
| 153 | 
            +
              "v",
         | 
| 154 | 
            +
              "w",
         | 
| 155 | 
            +
              "x",
         | 
| 156 | 
            +
              "y",
         | 
| 157 | 
            +
              "z",
         | 
| 158 | 
            +
              "#",
         | 
| 159 | 
            +
              "$",
         | 
| 160 | 
            +
              "%",
         | 
| 161 | 
            +
              "*",
         | 
| 162 | 
            +
              "+",
         | 
| 163 | 
            +
              ",",
         | 
| 164 | 
            +
              "-",
         | 
| 165 | 
            +
              ".",
         | 
| 166 | 
            +
              ":",
         | 
| 167 | 
            +
              ";",
         | 
| 168 | 
            +
              "=",
         | 
| 169 | 
            +
              "?",
         | 
| 170 | 
            +
              "@",
         | 
| 171 | 
            +
              "[",
         | 
| 172 | 
            +
              "]",
         | 
| 173 | 
            +
              "^",
         | 
| 174 | 
            +
              "_",
         | 
| 175 | 
            +
              "{",
         | 
| 176 | 
            +
              "|",
         | 
| 177 | 
            +
              "}",
         | 
| 178 | 
            +
              "~"
         | 
| 179 | 
            +
            ];
         | 
| 180 | 
            +
            const decode83 = (str) => {
         | 
| 181 | 
            +
              let value = 0;
         | 
| 182 | 
            +
              for (let i = 0; i < str.length; i++) {
         | 
| 183 | 
            +
                const c = str[i];
         | 
| 184 | 
            +
                const digit = digitCharacters.indexOf(c);
         | 
| 185 | 
            +
                value = value * 83 + digit;
         | 
| 186 | 
            +
              }
         | 
| 187 | 
            +
              return value;
         | 
| 188 | 
            +
            };
         | 
| 189 | 
            +
            const sRGBToLinear = (value) => {
         | 
| 190 | 
            +
              let v = value / 255;
         | 
| 191 | 
            +
              if (v <= 0.04045) {
         | 
| 192 | 
            +
                return v / 12.92;
         | 
| 193 | 
            +
              } else {
         | 
| 194 | 
            +
                return Math.pow((v + 0.055) / 1.055, 2.4);
         | 
| 195 | 
            +
              }
         | 
| 196 | 
            +
            };
         | 
| 197 | 
            +
            const linearTosRGB = (value) => {
         | 
| 198 | 
            +
              let v = Math.max(0, Math.min(1, value));
         | 
| 199 | 
            +
              if (v <= 31308e-7) {
         | 
| 200 | 
            +
                return Math.round(v * 12.92 * 255 + 0.5);
         | 
| 201 | 
            +
              } else {
         | 
| 202 | 
            +
                return Math.round((1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255 + 0.5);
         | 
| 203 | 
            +
              }
         | 
| 204 | 
            +
            };
         | 
| 205 | 
            +
            const sign = (n) => n < 0 ? -1 : 1;
         | 
| 206 | 
            +
            const signPow = (val, exp) => sign(val) * Math.pow(Math.abs(val), exp);
         | 
| 207 | 
            +
            class ValidationError extends Error {
         | 
| 208 | 
            +
              constructor(message) {
         | 
| 209 | 
            +
                super(message);
         | 
| 210 | 
            +
                this.name = "ValidationError";
         | 
| 211 | 
            +
                this.message = message;
         | 
| 212 | 
            +
              }
         | 
| 213 | 
            +
            }
         | 
| 214 | 
            +
            const validateBlurhash = (blurhash) => {
         | 
| 215 | 
            +
              if (!blurhash || blurhash.length < 6) {
         | 
| 216 | 
            +
                throw new ValidationError("The blurhash string must be at least 6 characters");
         | 
| 217 | 
            +
              }
         | 
| 218 | 
            +
              const sizeFlag = decode83(blurhash[0]);
         | 
| 219 | 
            +
              const numY = Math.floor(sizeFlag / 9) + 1;
         | 
| 220 | 
            +
              const numX = sizeFlag % 9 + 1;
         | 
| 221 | 
            +
              if (blurhash.length !== 4 + 2 * numX * numY) {
         | 
| 222 | 
            +
                throw new ValidationError(\`blurhash length mismatch: length is \${blurhash.length} but it should be \${4 + 2 * numX * numY}\`);
         | 
| 223 | 
            +
              }
         | 
| 224 | 
            +
            };
         | 
| 225 | 
            +
            const decodeDC = (value) => {
         | 
| 226 | 
            +
              const intR = value >> 16;
         | 
| 227 | 
            +
              const intG = value >> 8 & 255;
         | 
| 228 | 
            +
              const intB = value & 255;
         | 
| 229 | 
            +
              return [sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB)];
         | 
| 230 | 
            +
            };
         | 
| 231 | 
            +
            const decodeAC = (value, maximumValue) => {
         | 
| 232 | 
            +
              const quantR = Math.floor(value / (19 * 19));
         | 
| 233 | 
            +
              const quantG = Math.floor(value / 19) % 19;
         | 
| 234 | 
            +
              const quantB = value % 19;
         | 
| 235 | 
            +
              const rgb = [
         | 
| 236 | 
            +
                signPow((quantR - 9) / 9, 2) * maximumValue,
         | 
| 237 | 
            +
                signPow((quantG - 9) / 9, 2) * maximumValue,
         | 
| 238 | 
            +
                signPow((quantB - 9) / 9, 2) * maximumValue
         | 
| 239 | 
            +
              ];
         | 
| 240 | 
            +
              return rgb;
         | 
| 241 | 
            +
            };
         | 
| 242 | 
            +
            const decode = (blurhash, width, height, punch) => {
         | 
| 243 | 
            +
              validateBlurhash(blurhash);
         | 
| 244 | 
            +
              punch = punch | 1;
         | 
| 245 | 
            +
              const sizeFlag = decode83(blurhash[0]);
         | 
| 246 | 
            +
              const numY = Math.floor(sizeFlag / 9) + 1;
         | 
| 247 | 
            +
              const numX = sizeFlag % 9 + 1;
         | 
| 248 | 
            +
              const quantisedMaximumValue = decode83(blurhash[1]);
         | 
| 249 | 
            +
              const maximumValue = (quantisedMaximumValue + 1) / 166;
         | 
| 250 | 
            +
              const colors = new Array(numX * numY);
         | 
| 251 | 
            +
              for (let i = 0; i < colors.length; i++) {
         | 
| 252 | 
            +
                if (i === 0) {
         | 
| 253 | 
            +
                  const value = decode83(blurhash.substring(2, 6));
         | 
| 254 | 
            +
                  colors[i] = decodeDC(value);
         | 
| 255 | 
            +
                } else {
         | 
| 256 | 
            +
                  const value = decode83(blurhash.substring(4 + i * 2, 6 + i * 2));
         | 
| 257 | 
            +
                  colors[i] = decodeAC(value, maximumValue * punch);
         | 
| 258 | 
            +
                }
         | 
| 259 | 
            +
              }
         | 
| 260 | 
            +
              const bytesPerRow = width * 4;
         | 
| 261 | 
            +
              const pixels = new Uint8ClampedArray(bytesPerRow * height);
         | 
| 262 | 
            +
              for (let y = 0; y < height; y++) {
         | 
| 263 | 
            +
                for (let x = 0; x < width; x++) {
         | 
| 264 | 
            +
                  let r = 0;
         | 
| 265 | 
            +
                  let g = 0;
         | 
| 266 | 
            +
                  let b = 0;
         | 
| 267 | 
            +
                  for (let j = 0; j < numY; j++) {
         | 
| 268 | 
            +
                    for (let i = 0; i < numX; i++) {
         | 
| 269 | 
            +
                      const basis = Math.cos(Math.PI * x * i / width) * Math.cos(Math.PI * y * j / height);
         | 
| 270 | 
            +
                      let color = colors[i + j * numX];
         | 
| 271 | 
            +
                      r += color[0] * basis;
         | 
| 272 | 
            +
                      g += color[1] * basis;
         | 
| 273 | 
            +
                      b += color[2] * basis;
         | 
| 274 | 
            +
                    }
         | 
| 275 | 
            +
                  }
         | 
| 276 | 
            +
                  let intR = linearTosRGB(r);
         | 
| 277 | 
            +
                  let intG = linearTosRGB(g);
         | 
| 278 | 
            +
                  let intB = linearTosRGB(b);
         | 
| 279 | 
            +
                  pixels[4 * x + 0 + y * bytesPerRow] = intR;
         | 
| 280 | 
            +
                  pixels[4 * x + 1 + y * bytesPerRow] = intG;
         | 
| 281 | 
            +
                  pixels[4 * x + 2 + y * bytesPerRow] = intB;
         | 
| 282 | 
            +
                  pixels[4 * x + 3 + y * bytesPerRow] = 255;
         | 
| 283 | 
            +
                }
         | 
| 284 | 
            +
              }
         | 
| 285 | 
            +
              return pixels;
         | 
| 286 | 
            +
            };
         | 
| 287 | 
            +
            var decode$1 = decode;
         | 
| 288 | 
            +
            addEventListener("message", (evt) => {
         | 
| 289 | 
            +
              console.log(evt);
         | 
| 290 | 
            +
              const { canvas, width, height, blurhash } = evt.data;
         | 
| 291 | 
            +
              console.log("Encoding", blurhash);
         | 
| 292 | 
            +
              const start = +new Date();
         | 
| 293 | 
            +
              const pixels = decode$1(blurhash, width, height);
         | 
| 294 | 
            +
              const ctx = canvas.getContext("2d");
         | 
| 295 | 
            +
              const imageData = ctx.createImageData(width, height);
         | 
| 296 | 
            +
              imageData.data.set(pixels);
         | 
| 297 | 
            +
              ctx.putImageData(imageData, 0, 0);
         | 
| 298 | 
            +
              const end = +new Date();
         | 
| 299 | 
            +
              console.log("Done Encoding", blurhash, end - start);
         | 
| 300 | 
            +
            }); })();` },
         | 
| 13 301 | 
             
              })
         | 
| 14 302 | 
             
            );
         | 
    
        package/package.json
    CHANGED
    
    
    
        package/vite.wc.config.ts
    CHANGED