next-blurhash-previews 0.0.3-beta55 → 0.0.3-beta56

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,6 @@
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";
@@ -13,6 +16,18 @@ export async function getSharpImage(imgPath) {
13
16
 
14
17
  return sharp(buffer);
15
18
  } else {
19
+ const ext = path.extname(imgPath);
20
+ const dir = path.dirname(imgPath);
21
+ const basename = path.basename(imgPath, ext);
22
+
23
+ const previewOption = path.join(dir, basename + "-preview", ext);
24
+ console.log("Trying preview", previewOption);
25
+
26
+ if (fs.existsSync()) {
27
+ console.log("Preview found");
28
+ return sharp(previewOption);
29
+ }
30
+
16
31
  return sharp(imgPath);
17
32
  }
18
33
  }
@@ -4,11 +4,299 @@ export const imagePreviewBootstrap = createElement(
4
4
  Fragment,
5
5
  {},
6
6
  createElement("script", {
7
- dangerouslySetInnerHTML: { __html: `(() => { "use strict";class a extends HTMLElement{sd;mo;static observedAttributes=["preview"];#s=!1;get#e(){return this.querySelector("img")}get#t(){return this.querySelector("canvas")}constructor(){super(),this.sd=this.attachShadow({mode:"open"}),this.sd.innerHTML='<slot name="preview"></slot>'}#i=()=>{if(this.#e&&this.#t)return this.mo?.disconnect(),this.#e.complete?this.#r():(this.#h(),this.#e.addEventListener("load",this.#r)),1};connectedCallback(){this.#s=!0,this.#i()||(this.mo=new MutationObserver(this.#i),this.mo.observe(this,{subtree:!0,childList:!0,attributes:!1}))}#r=()=>{this.sd.innerHTML='<slot name="image"></slot>'};attributeChangedCallback(e){this.#t&&e==="preview"&&this.#h()}#h(){if(!this.#s||!this.getAttribute("preview"))return;const e=JSON.parse(this.getAttribute("preview"));c(this.#t,e)}}customElements.get("blurhash-image")||customElements.define("blurhash-image",a);function c(t,e){const{w:s,h:i,blurhash:h}=e;t.width=s,t.height=i;{const n=new Blob([document.querySelector("#next-blurhash-worker-script").textContent],{type:"text/javascript"}),o=new Worker(window.URL.createObjectURL(n)),r=t.transferControlToOffscreen();o.postMessage({canvas:r,width:s,height:i,blurhash:h},[r])}} })();` },
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";const I=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","#","$","%","*","+",",","-",".",":",";","=","?","@","[","]","^","_","{","|","}","~"],u=t=>{let n=0;for(let o=0;o<t.length;o++){const e=t[o],c=I.indexOf(e);n=n*83+c}return n},D=t=>{let n=t/255;return n<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)},b=t=>{let n=Math.max(0,Math.min(1,t));return n<=.0031308?Math.round(n*12.92*255+.5):Math.round((1.055*Math.pow(n,.4166666666666667)-.055)*255+.5)},P=t=>t<0?-1:1,p=(t,n)=>P(t)*Math.pow(Math.abs(t),n);class q extends Error{constructor(n){super(n),this.name="ValidationError",this.message=n}}const v=t=>{if(!t||t.length<6)throw new q("The blurhash string must be at least 6 characters");const n=u(t[0]),o=Math.floor(n/9)+1,e=n%9+1;if(t.length!==4+2*e*o)throw new q(\`blurhash length mismatch: length is \${t.length} but it should be \${4+2*e*o}\`)},A=t=>{const n=t>>16,o=t>>8&255,e=t&255;return[D(n),D(o),D(e)]},T=(t,n)=>{const o=Math.floor(t/361),e=Math.floor(t/19)%19,c=t%19;return[p((o-9)/9,2)*n,p((e-9)/9,2)*n,p((c-9)/9,2)*n]},V=(t,n,o,e)=>{v(t),e=e|1;const c=u(t[0]),i=Math.floor(c/9)+1,l=c%9+1,h=(u(t[1])+1)/166,r=new Array(l*i);for(let s=0;s<r.length;s++)if(s===0){const a=u(t.substring(2,6));r[s]=A(a)}else{const a=u(t.substring(4+s*2,6+s*2));r[s]=T(a,h*e)}const d=n*4,g=new Uint8ClampedArray(d*o);for(let s=0;s<o;s++)for(let a=0;a<n;a++){let B=0,E=0,R=0;for(let M=0;M<i;M++)for(let f=0;f<l;f++){const w=Math.cos(Math.PI*a*f/n)*Math.cos(Math.PI*s*M/o);let x=r[f+M*l];B+=x[0]*w,E+=x[1]*w,R+=x[2]*w}let C=b(B),G=b(E),y=b(R);g[4*a+0+s*d]=C,g[4*a+1+s*d]=G,g[4*a+2+s*d]=y,g[4*a+3+s*d]=255}return g};var $=V;addEventListener("message",t=>{console.log(t);const{canvas:n,width:o,height:e,blurhash:c}=t.data;console.log("Encoding",c);const i=+new Date,l=$(c,o,e),m=n.getContext("2d"),h=m.createImageData(o,e);h.data.set(l),m.putImageData(h,0,0);const r=+new Date;console.log("Done Encoding",c,r-i)}); })();` },
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-blurhash-previews",
3
- "version": "0.0.3-beta55",
3
+ "version": "0.0.3-beta56",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
package/vite.wc.config.ts CHANGED
@@ -12,7 +12,7 @@ export default defineConfig({
12
12
  fileName: () => "imageWithPreview.js",
13
13
  name: "imageWithPreview",
14
14
  },
15
- //minify: false,
15
+ minify: false,
16
16
  },
17
17
  plugins: [react()],
18
18
  });
@@ -12,7 +12,7 @@ export default defineConfig({
12
12
  fileName: () => "workerScript.js",
13
13
  name: "workerScript",
14
14
  },
15
- //minify: false,
15
+ minify: false,
16
16
  },
17
17
  plugins: [react()],
18
18
  });