@tsparticles/shape-image 3.1.0 → 3.2.1

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.
Files changed (42) hide show
  1. package/21.min.js +2 -0
  2. package/21.min.js.LICENSE.txt +1 -0
  3. package/618.min.js +2 -0
  4. package/618.min.js.LICENSE.txt +1 -0
  5. package/623.min.js +2 -0
  6. package/623.min.js.LICENSE.txt +1 -0
  7. package/browser/GifUtils/ByteStream.js +2 -1
  8. package/browser/GifUtils/Utils.js +96 -0
  9. package/browser/ImageDrawer.js +42 -113
  10. package/browser/ImagePreloader.js +2 -1
  11. package/browser/Utils.js +1 -20
  12. package/browser/index.js +3 -3
  13. package/cjs/GifUtils/ByteStream.js +2 -1
  14. package/cjs/GifUtils/Utils.js +122 -1
  15. package/cjs/ImageDrawer.js +64 -112
  16. package/cjs/ImagePreloader.js +2 -1
  17. package/cjs/Utils.js +2 -22
  18. package/cjs/index.js +28 -5
  19. package/dist_browser_GifUtils_Utils_js.js +50 -0
  20. package/dist_browser_ImageDrawer_js.js +30 -0
  21. package/dist_browser_ImagePreloader_js.js +40 -0
  22. package/esm/GifUtils/ByteStream.js +2 -1
  23. package/esm/GifUtils/Utils.js +96 -0
  24. package/esm/ImageDrawer.js +42 -113
  25. package/esm/ImagePreloader.js +2 -1
  26. package/esm/Utils.js +1 -20
  27. package/esm/index.js +3 -3
  28. package/package.json +2 -2
  29. package/report.html +3 -3
  30. package/tsparticles.shape.image.js +251 -910
  31. package/tsparticles.shape.image.min.js +1 -1
  32. package/tsparticles.shape.image.min.js.LICENSE.txt +1 -1
  33. package/types/GifUtils/Utils.d.ts +4 -0
  34. package/types/ImageDrawer.d.ts +4 -4
  35. package/types/ImagePreloader.d.ts +1 -1
  36. package/types/Utils.d.ts +0 -1
  37. package/umd/GifUtils/ByteStream.js +2 -1
  38. package/umd/GifUtils/Utils.js +123 -1
  39. package/umd/ImageDrawer.js +66 -113
  40. package/umd/ImagePreloader.js +2 -1
  41. package/umd/Utils.js +3 -23
  42. package/umd/index.js +30 -6
package/21.min.js ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 21.min.js.LICENSE.txt */
2
+ (this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[21],{21:(e,a,i)=>{i.d(a,{ImageDrawer:()=>s});var t=i(533);class s{constructor(e){this.loadImageShape=async e=>{if(!this._engine.loadImage)throw new Error(`${t.errorPrefix} image shape not initialized`);await this._engine.loadImage({gif:e.gif,name:e.name,replaceColor:e.replaceColor??!1,src:e.src})},this._engine=e}addImage(e){this._engine.images||(this._engine.images=[]),this._engine.images.push(e)}async draw(e){const{context:a,radius:t,particle:s,opacity:n}=e,o=s.image,r=o?.element;if(o){if(a.globalAlpha=n,o.gif&&o.gifData){const{drawGif:a}=await i.e(618).then(i.bind(i,618));a(e)}else if(r){const e=o.ratio,i={x:-t,y:-t},s=2*t;a.drawImage(r,i.x,i.y,s,s/e)}a.globalAlpha=1}}getSidesCount(){return 12}async init(e){const a=e.actualOptions;if(a.preload&&this._engine.loadImage)for(const e of a.preload)await this._engine.loadImage(e)}async loadShape(e){if("image"!==e.shape&&"images"!==e.shape)return;this._engine.images||(this._engine.images=[]);const a=e.shapeData;if(!a)return;this._engine.images.find((e=>e.name===a.name||e.source===a.src))||(await this.loadImageShape(a),await this.loadShape(e))}async particleInit(e,a){if("image"!==a.shape&&"images"!==a.shape)return;this._engine.images||(this._engine.images=[]);const t=this._engine.images,s=a.shapeData;if(!s)return;const n=a.getFillColor(),o=t.find((e=>e.name===s.name||e.source===s.src));if(!o)return;const r=s.replaceColor??o.replaceColor;if(o.loading)return void setTimeout((()=>{this.particleInit(e,a)}));let l;if(o.svgData&&n){const{replaceImageColor:e}=await Promise.resolve().then(i.bind(i,103));l=await e(o,s,n,a)}else l={color:n,data:o,element:o.element,gif:o.gif,gifData:o.gifData,gifLoopCount:o.gifLoopCount,loaded:!0,ratio:s.width&&s.height?s.width/s.height:o.ratio??1,replaceColor:r,source:s.src};l.ratio||(l.ratio=1);const g={image:l,fill:s.fill??a.shapeFill,close:s.close??a.shapeClose};a.image=g.image,a.shapeFill=g.fill,a.shapeClose=g.close}}}}]);
@@ -0,0 +1 @@
1
+ /*! tsParticles Image Shape v3.2.1 by Matteo Bruni */
package/618.min.js ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 618.min.js.LICENSE.txt */
2
+ (this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[618],{618:(t,e,a)=>{a.d(e,{drawGif:()=>p,loadGifImage:()=>w});const o=[0,4,2,1],i=[8,8,4,2];class r{constructor(t){this.pos=0,this.data=new Uint8ClampedArray(t)}getString(t){const e=this.data.slice(this.pos,this.pos+t);return this.pos+=e.length,e.reduce(((t,e)=>t+String.fromCharCode(e)),"")}nextByte(){return this.data[this.pos++]}nextTwoBytes(){return this.pos+=2,this.data[this.pos-2]+(this.data[this.pos-1]<<8)}readSubBlocks(){let t="",e=0;do{e=this.data[this.pos++];for(let a=e;--a>=0;t+=String.fromCharCode(this.data[this.pos++]));}while(0!==e);return t}readSubBlocksBin(){let t=this.data[this.pos],e=0;for(let a=0;0!==t;a+=t+1,t=this.data[this.pos+a])e+=t;const a=new Uint8Array(e);t=this.data[this.pos++];for(let e=0;0!==t;t=this.data[this.pos++])for(let o=t;--o>=0;a[e++]=this.data[this.pos++]);return a}skipSubBlocks(){for(const t=1,e=0;this.data[this.pos]!==e;this.pos+=this.data[this.pos]+t);this.pos++}}const s={x:0,y:0},n=0,l=.5,h=0,c=0,g=0;function f(t,e){const a=[];for(let o=0;o<e;o++)a.push({r:t.data[t.pos],g:t.data[t.pos+1],b:t.data[t.pos+2]}),t.pos+=3;return a}async function d(t,e,a,r,s,n){switch(t.nextByte()){case 59:return!0;case 44:await async function(t,e,a,r,s,n){const l=e.frames[r(!0)];l.left=t.nextTwoBytes(),l.top=t.nextTwoBytes(),l.width=t.nextTwoBytes(),l.height=t.nextTwoBytes();const h=t.nextByte(),c=128==(128&h),g=64==(64&h);l.sortFlag=32==(32&h),l.reserved=(24&h)>>>3;const d=1<<1+(7&h);c&&(l.localColorTable=f(t,d));const p=t=>{const{r:o,g:i,b:r}=(c?l.localColorTable:e.globalColorTable)[t];return t!==s(null)?{r:o,g:i,b:r,a:255}:{r:o,g:i,b:r,a:a?~~((o+i+r)/3):0}},w=(()=>{try{return new ImageData(l.width,l.height,{colorSpace:"srgb"})}catch(t){if(t instanceof DOMException&&"IndexSizeError"===t.name)return null;throw t}})();if(null==w)throw new EvalError("GIF frame size is to large");const u=t.nextByte(),m=t.readSubBlocksBin(),y=1<<u,b=(t,e)=>{const a=t>>>3,o=7&t;return(m[a]+(m[a+1]<<8)+(m[a+2]<<16)&(1<<e)-1<<o)>>>o};if(g){for(let a=0,s=u+1,h=0,c=[[0]],g=0;g<4;g++){if(o[g]<l.height){let t=0,e=0,r=!1;for(;!r;){const n=a;if(a=b(h,s),h+=s+1,a===y){s=u+1,c.length=y+2;for(let t=0;t<c.length;t++)c[t]=t<y?[t]:[]}else{a>=c.length?c.push(c[n].concat(c[n][0])):n!==y&&c.push(c[n].concat(c[a][0]));for(const r of c[a]){const{r:a,g:s,b:n,a:h}=p(r);w.data.set([a,s,n,h],o[g]*l.width+i[g]*e+t%(4*l.width)),t+=4}c.length===1<<s&&s<12&&s++}t===4*l.width*(e+1)&&(e++,o[g]+i[g]*e>=l.height&&(r=!0))}}n?.(t.pos/(t.data.length-1),r(!1)+1,w,{x:l.left,y:l.top},{width:e.width,height:e.height})}l.image=w,l.bitmap=await createImageBitmap(w)}else{let a=0,o=u+1,i=0,s=-4,h=!1;const c=[[0]];for(;!h;){const t=a;if(a=b(i,o),i+=o,a===y){o=u+1,c.length=y+2;for(let t=0;t<c.length;t++)c[t]=t<y?[t]:[]}else{if(a===y+1){h=!0;break}a>=c.length?c.push(c[t].concat(c[t][0])):t!==y&&c.push(c[t].concat(c[a][0]));for(const t of c[a]){const{r:e,g:a,b:o,a:i}=p(t);w.data.set([e,a,o,i],s+=4)}c.length>=1<<o&&o<12&&o++}}l.image=w,l.bitmap=await createImageBitmap(w),n?.((t.pos+1)/t.data.length,r(!1)+1,l.image,{x:l.left,y:l.top},{width:e.width,height:e.height})}}(t,e,a,r,s,n);break;case 33:!function(t,e,a,o){switch(t.nextByte()){case 249:{const i=e.frames[a(!1)];t.pos++;const r=t.nextByte();i.GCreserved=(224&r)>>>5,i.disposalMethod=(28&r)>>>2,i.userInputDelayFlag=2==(2&r);const s=1==(1&r);i.delayTime=10*t.nextTwoBytes();const n=t.nextByte();s&&o(n),t.pos++;break}case 255:{t.pos++;const a={identifier:t.getString(8),authenticationCode:t.getString(3),data:t.readSubBlocksBin()};e.applicationExtensions.push(a);break}case 254:e.comments.push([a(!1),t.readSubBlocks()]);break;case 1:if(0===e.globalColorTable.length)throw new EvalError("plain text extension without global color table");t.pos++,e.frames[a(!1)].plainTextData={left:t.nextTwoBytes(),top:t.nextTwoBytes(),width:t.nextTwoBytes(),height:t.nextTwoBytes(),charSize:{width:t.nextTwoBytes(),height:t.nextTwoBytes()},foregroundColor:t.nextByte(),backgroundColor:t.nextByte(),text:t.readSubBlocks()};break;default:t.skipSubBlocks()}}(t,e,r,s);break;default:throw new EvalError("undefined block found")}return!1}function p(t){const{context:e,radius:a,particle:o,delta:i}=t,r=o.image;if(!r?.gifData||!r.gif)return;const f=new OffscreenCanvas(r.gifData.width,r.gifData.height),d=f.getContext("2d");if(!d)throw new Error("could not create offscreen canvas context");d.imageSmoothingQuality="low",d.imageSmoothingEnabled=!1,d.clearRect(s.x,s.y,f.width,f.height),void 0===o.gifLoopCount&&(o.gifLoopCount=r.gifLoopCount??g);let p=o.gifFrame??n;const w={x:-r.gifData.width*l,y:-r.gifData.height*l},u=r.gifData.frames[p];if(void 0===o.gifTime&&(o.gifTime=h),u.bitmap){switch(e.scale(a/r.gifData.width,a/r.gifData.height),u.disposalMethod){case 4:case 5:case 6:case 7:case 0:d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y),d.clearRect(s.x,s.y,f.width,f.height);break;case 1:d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y);break;case 2:d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y),d.clearRect(s.x,s.y,f.width,f.height),r.gifData.globalColorTable.length?d.putImageData(r.gifData.backgroundImage,w.x,w.y):d.putImageData(r.gifData.frames[c].image,w.x+u.left,w.y+u.top);break;case 3:{const t=d.getImageData(s.x,s.y,f.width,f.height);d.drawImage(u.bitmap,u.left,u.top),e.drawImage(f,w.x,w.y),d.clearRect(s.x,s.y,f.width,f.height),d.putImageData(t,s.x,s.y)}}if(o.gifTime+=i.value,o.gifTime>u.delayTime){if(o.gifTime-=u.delayTime,++p>=r.gifData.frames.length){if(--o.gifLoopCount<=g)return;p=c,d.clearRect(s.x,s.y,f.width,f.height)}o.gifFrame=p}e.scale(r.gifData.width/a,r.gifData.height/a)}}async function w(t){if("gif"===t.type){t.loading=!0;try{t.gifData=await async function(t,e,a){a||(a=!1);const o=await fetch(t);if(!o.ok&&404===o.status)throw new EvalError("file not found");const i=await o.arrayBuffer(),s={width:0,height:0,totalTime:0,colorRes:0,pixelAspectRatio:0,frames:[],sortFlag:!1,globalColorTable:[],backgroundImage:new ImageData(1,1,{colorSpace:"srgb"}),comments:[],applicationExtensions:[]},n=new r(new Uint8ClampedArray(i));if("GIF89a"!==n.getString(6))throw new Error("not a supported GIF file");s.width=n.nextTwoBytes(),s.height=n.nextTwoBytes();const l=n.nextByte(),h=128==(128&l);s.colorRes=(112&l)>>>4,s.sortFlag=8==(8&l);const c=1<<1+(7&l),g=n.nextByte();s.pixelAspectRatio=n.nextByte(),0!==s.pixelAspectRatio&&(s.pixelAspectRatio=(s.pixelAspectRatio+15)/64),h&&(s.globalColorTable=f(n,c));const p=(()=>{try{return new ImageData(s.width,s.height,{colorSpace:"srgb"})}catch(t){if(t instanceof DOMException&&"IndexSizeError"===t.name)return null;throw t}})();if(null==p)throw new Error("GIF frame size is to large");const{r:w,g:u,b:m}=s.globalColorTable[g];p.data.set(h?[w,u,m,255]:[0,0,0,0]);for(let t=4;t<p.data.length;t*=2)p.data.copyWithin(t,0,t);s.backgroundImage=p;let y=-1,b=!0,x=-1;const B=t=>(t&&(b=!0),y),T=t=>(null!=t&&(x=t),x);try{do{b&&(s.frames.push({left:0,top:0,width:0,height:0,disposalMethod:0,image:new ImageData(1,1,{colorSpace:"srgb"}),plainTextData:null,userInputDelayFlag:!1,delayTime:0,sortFlag:!1,localColorTable:[],reserved:0,GCreserved:0}),y++,x=-1,b=!1)}while(!await d(n,s,a,B,T,e));s.frames.length--;for(const t of s.frames){if(t.userInputDelayFlag&&0===t.delayTime){s.totalTime=1/0;break}s.totalTime+=t.delayTime}return s}catch(t){if(t instanceof EvalError)throw new Error(`error while parsing frame ${y} "${t.message}"`);throw t}}(t.source),t.gifLoopCount=function(t){for(const e of t.applicationExtensions)if(e.identifier+e.authenticationCode==="NETSCAPE2.0")return e.data[1]+(e.data[2]<<8);return NaN}(t.gifData)??g,t.gifLoopCount||(t.gifLoopCount=1/0)}catch{t.error=!0}t.loading=!1}else{const{loadImage:e}=await Promise.resolve().then(a.bind(a,103));await e(t)}}}}]);
@@ -0,0 +1 @@
1
+ /*! tsParticles Image Shape v3.2.1 by Matteo Bruni */
package/623.min.js ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 623.min.js.LICENSE.txt */
2
+ (this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[623],{623:(e,i,s)=>{s.d(i,{ImagePreloaderPlugin:()=>r});class o{constructor(){this.src="",this.gif=!1}load(e){e&&(void 0!==e.gif&&(this.gif=e.gif),void 0!==e.height&&(this.height=e.height),void 0!==e.name&&(this.name=e.name),void 0!==e.replaceColor&&(this.replaceColor=e.replaceColor),void 0!==e.src&&(this.src=e.src),void 0!==e.width&&(this.width=e.width))}}class r{constructor(e){this.id="imagePreloader",this._engine=e}async getPlugin(){return await Promise.resolve(),{}}loadOptions(e,i){if(!i?.preload)return;e.preload||(e.preload=[]);const s=e.preload;for(const e of i.preload){const i=s.find((i=>i.name===e.name||i.src===e.src));if(i)i.load(e);else{const i=new o;i.load(e),s.push(i)}}}needsPlugin(){return!0}}}}]);
@@ -0,0 +1 @@
1
+ /*! tsParticles Image Shape v3.2.1 by Matteo Bruni */
@@ -27,12 +27,13 @@ export class ByteStream {
27
27
  return blockString;
28
28
  }
29
29
  readSubBlocksBin() {
30
- let size = 0, len = 0;
30
+ let size = this.data[this.pos], len = 0;
31
31
  const emptySize = 0, increment = 1;
32
32
  for (let offset = 0; size !== emptySize; offset += size + increment, size = this.data[this.pos + offset]) {
33
33
  len += size;
34
34
  }
35
35
  const blockData = new Uint8Array(len);
36
+ size = this.data[this.pos++];
36
37
  for (let i = 0; size !== emptySize; size = this.data[this.pos++]) {
37
38
  for (let count = size; --count >= emptySize; blockData[i++] = this.data[this.pos++]) {
38
39
  }
@@ -1,5 +1,9 @@
1
1
  import { InterlaceOffsets, InterlaceSteps } from "./Constants.js";
2
2
  import { ByteStream } from "./ByteStream.js";
3
+ const origin = {
4
+ x: 0,
5
+ y: 0,
6
+ }, defaultFrame = 0, half = 0.5, initialTime = 0, firstIndex = 0, defaultLoopCount = 0;
3
7
  function parseColorTable(byteStream, count) {
4
8
  const colors = [];
5
9
  for (let i = 0; i < count; i++) {
@@ -329,3 +333,95 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
329
333
  throw error;
330
334
  }
331
335
  }
336
+ export function drawGif(data) {
337
+ const { context, radius, particle, delta } = data, image = particle.image;
338
+ if (!image?.gifData || !image.gif) {
339
+ return;
340
+ }
341
+ const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
342
+ if (!offscreenContext) {
343
+ throw new Error("could not create offscreen canvas context");
344
+ }
345
+ offscreenContext.imageSmoothingQuality = "low";
346
+ offscreenContext.imageSmoothingEnabled = false;
347
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
348
+ if (particle.gifLoopCount === undefined) {
349
+ particle.gifLoopCount = image.gifLoopCount ?? defaultLoopCount;
350
+ }
351
+ let frameIndex = particle.gifFrame ?? defaultFrame;
352
+ const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
353
+ if (particle.gifTime === undefined) {
354
+ particle.gifTime = initialTime;
355
+ }
356
+ if (!frame.bitmap) {
357
+ return;
358
+ }
359
+ context.scale(radius / image.gifData.width, radius / image.gifData.height);
360
+ switch (frame.disposalMethod) {
361
+ case 4:
362
+ case 5:
363
+ case 6:
364
+ case 7:
365
+ case 0:
366
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
367
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
368
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
369
+ break;
370
+ case 1:
371
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
372
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
373
+ break;
374
+ case 2:
375
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
376
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
377
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
378
+ if (!image.gifData.globalColorTable.length) {
379
+ offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
380
+ }
381
+ else {
382
+ offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
383
+ }
384
+ break;
385
+ case 3:
386
+ {
387
+ const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
388
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
389
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
390
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
391
+ offscreenContext.putImageData(previousImageData, origin.x, origin.y);
392
+ }
393
+ break;
394
+ }
395
+ particle.gifTime += delta.value;
396
+ if (particle.gifTime > frame.delayTime) {
397
+ particle.gifTime -= frame.delayTime;
398
+ if (++frameIndex >= image.gifData.frames.length) {
399
+ if (--particle.gifLoopCount <= defaultLoopCount) {
400
+ return;
401
+ }
402
+ frameIndex = firstIndex;
403
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
404
+ }
405
+ particle.gifFrame = frameIndex;
406
+ }
407
+ context.scale(image.gifData.width / radius, image.gifData.height / radius);
408
+ }
409
+ export async function loadGifImage(image) {
410
+ if (image.type !== "gif") {
411
+ const { loadImage } = await import("../Utils.js");
412
+ await loadImage(image);
413
+ return;
414
+ }
415
+ image.loading = true;
416
+ try {
417
+ image.gifData = await decodeGIF(image.source);
418
+ image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? defaultLoopCount;
419
+ if (!image.gifLoopCount) {
420
+ image.gifLoopCount = Infinity;
421
+ }
422
+ }
423
+ catch {
424
+ image.error = true;
425
+ }
426
+ image.loading = false;
427
+ }
@@ -1,9 +1,5 @@
1
- import { errorPrefix, } from "@tsparticles/engine";
2
- import { replaceImageColor } from "./Utils.js";
3
- const origin = {
4
- x: 0,
5
- y: 0,
6
- }, defaultLoopCount = 0, defaultFrame = 0, half = 0.5, initialTime = 0, firstIndex = 0, double = 2, defaultAlpha = 1, sides = 12, defaultRatio = 1;
1
+ import { errorPrefix } from "@tsparticles/engine";
2
+ const double = 2, defaultAlpha = 1, sides = 12, defaultRatio = 1;
7
3
  export class ImageDrawer {
8
4
  constructor(engine) {
9
5
  this.loadImageShape = async (imageShape) => {
@@ -25,80 +21,15 @@ export class ImageDrawer {
25
21
  }
26
22
  this._engine.images.push(image);
27
23
  }
28
- draw(data) {
29
- const { context, radius, particle, opacity, delta } = data, image = particle.image, element = image?.element;
24
+ async draw(data) {
25
+ const { context, radius, particle, opacity } = data, image = particle.image, element = image?.element;
30
26
  if (!image) {
31
27
  return;
32
28
  }
33
29
  context.globalAlpha = opacity;
34
30
  if (image.gif && image.gifData) {
35
- const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
36
- if (!offscreenContext) {
37
- throw new Error("could not create offscreen canvas context");
38
- }
39
- offscreenContext.imageSmoothingQuality = "low";
40
- offscreenContext.imageSmoothingEnabled = false;
41
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
42
- if (particle.gifLoopCount === undefined) {
43
- particle.gifLoopCount = image.gifLoopCount ?? defaultLoopCount;
44
- }
45
- let frameIndex = particle.gifFrame ?? defaultFrame;
46
- const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
47
- if (particle.gifTime === undefined) {
48
- particle.gifTime = initialTime;
49
- }
50
- if (!frame.bitmap) {
51
- return;
52
- }
53
- context.scale(radius / image.gifData.width, radius / image.gifData.height);
54
- switch (frame.disposalMethod) {
55
- case 4:
56
- case 5:
57
- case 6:
58
- case 7:
59
- case 0:
60
- offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
61
- context.drawImage(offscreenCanvas, pos.x, pos.y);
62
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
63
- break;
64
- case 1:
65
- offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
66
- context.drawImage(offscreenCanvas, pos.x, pos.y);
67
- break;
68
- case 2:
69
- offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
70
- context.drawImage(offscreenCanvas, pos.x, pos.y);
71
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
72
- if (!image.gifData.globalColorTable.length) {
73
- offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
74
- }
75
- else {
76
- offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
77
- }
78
- break;
79
- case 3:
80
- {
81
- const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
82
- offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
83
- context.drawImage(offscreenCanvas, pos.x, pos.y);
84
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
85
- offscreenContext.putImageData(previousImageData, origin.x, origin.y);
86
- }
87
- break;
88
- }
89
- particle.gifTime += delta.value;
90
- if (particle.gifTime > frame.delayTime) {
91
- particle.gifTime -= frame.delayTime;
92
- if (++frameIndex >= image.gifData.frames.length) {
93
- if (--particle.gifLoopCount <= defaultLoopCount) {
94
- return;
95
- }
96
- frameIndex = firstIndex;
97
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
98
- }
99
- particle.gifFrame = frameIndex;
100
- }
101
- context.scale(image.gifData.width / radius, image.gifData.height / radius);
31
+ const { drawGif } = await import("./GifUtils/Utils.js");
32
+ drawGif(data);
102
33
  }
103
34
  else if (element) {
104
35
  const ratio = image.ratio, pos = {
@@ -121,7 +52,7 @@ export class ImageDrawer {
121
52
  await this._engine.loadImage(imageData);
122
53
  }
123
54
  }
124
- loadShape(particle) {
55
+ async loadShape(particle) {
125
56
  if (particle.shape !== "image" && particle.shape !== "images") {
126
57
  return;
127
58
  }
@@ -134,12 +65,11 @@ export class ImageDrawer {
134
65
  }
135
66
  const image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src);
136
67
  if (!image) {
137
- void this.loadImageShape(imageData).then(() => {
138
- this.loadShape(particle);
139
- });
68
+ await this.loadImageShape(imageData);
69
+ await this.loadShape(particle);
140
70
  }
141
71
  }
142
- particleInit(container, particle) {
72
+ async particleInit(container, particle) {
143
73
  if (particle.shape !== "image" && particle.shape !== "images") {
144
74
  return;
145
75
  }
@@ -157,42 +87,41 @@ export class ImageDrawer {
157
87
  const replaceColor = imageData.replaceColor ?? image.replaceColor;
158
88
  if (image.loading) {
159
89
  setTimeout(() => {
160
- this.particleInit(container, particle);
90
+ void this.particleInit(container, particle);
161
91
  });
162
92
  return;
163
93
  }
164
- void (async () => {
165
- let imageRes;
166
- if (image.svgData && color) {
167
- imageRes = await replaceImageColor(image, imageData, color, particle);
168
- }
169
- else {
170
- imageRes = {
171
- color,
172
- data: image,
173
- element: image.element,
174
- gif: image.gif,
175
- gifData: image.gifData,
176
- gifLoopCount: image.gifLoopCount,
177
- loaded: true,
178
- ratio: imageData.width && imageData.height
179
- ? imageData.width / imageData.height
180
- : image.ratio ?? defaultRatio,
181
- replaceColor: replaceColor,
182
- source: imageData.src,
183
- };
184
- }
185
- if (!imageRes.ratio) {
186
- imageRes.ratio = 1;
187
- }
188
- const fill = imageData.fill ?? particle.shapeFill, close = imageData.close ?? particle.shapeClose, imageShape = {
189
- image: imageRes,
190
- fill,
191
- close,
94
+ let imageRes;
95
+ if (image.svgData && color) {
96
+ const { replaceImageColor } = await import("./Utils.js");
97
+ imageRes = await replaceImageColor(image, imageData, color, particle);
98
+ }
99
+ else {
100
+ imageRes = {
101
+ color,
102
+ data: image,
103
+ element: image.element,
104
+ gif: image.gif,
105
+ gifData: image.gifData,
106
+ gifLoopCount: image.gifLoopCount,
107
+ loaded: true,
108
+ ratio: imageData.width && imageData.height
109
+ ? imageData.width / imageData.height
110
+ : image.ratio ?? defaultRatio,
111
+ replaceColor: replaceColor,
112
+ source: imageData.src,
192
113
  };
193
- particle.image = imageShape.image;
194
- particle.shapeFill = imageShape.fill;
195
- particle.shapeClose = imageShape.close;
196
- })();
114
+ }
115
+ if (!imageRes.ratio) {
116
+ imageRes.ratio = 1;
117
+ }
118
+ const fill = imageData.fill ?? particle.shapeFill, close = imageData.close ?? particle.shapeClose, imageShape = {
119
+ image: imageRes,
120
+ fill,
121
+ close,
122
+ };
123
+ particle.image = imageShape.image;
124
+ particle.shapeFill = imageShape.fill;
125
+ particle.shapeClose = imageShape.close;
197
126
  }
198
127
  }
@@ -4,7 +4,8 @@ export class ImagePreloaderPlugin {
4
4
  this.id = "imagePreloader";
5
5
  this._engine = engine;
6
6
  }
7
- getPlugin() {
7
+ async getPlugin() {
8
+ await Promise.resolve();
8
9
  return {};
9
10
  }
10
11
  loadOptions(options, source) {
package/browser/Utils.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { errorPrefix, getLogger, getStyleFromHsl } from "@tsparticles/engine";
2
- import { decodeGIF, getGIFLoopAmount } from "./GifUtils/Utils.js";
3
- const stringStart = 0, defaultLoopCount = 0, defaultOpacity = 1;
2
+ const stringStart = 0, defaultOpacity = 1;
4
3
  const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;
5
4
  function replaceColorSvg(imageShape, color, opacity) {
6
5
  const { svgData } = imageShape;
@@ -33,24 +32,6 @@ export async function loadImage(image) {
33
32
  img.src = image.source;
34
33
  });
35
34
  }
36
- export async function loadGifImage(image) {
37
- if (image.type !== "gif") {
38
- await loadImage(image);
39
- return;
40
- }
41
- image.loading = true;
42
- try {
43
- image.gifData = await decodeGIF(image.source);
44
- image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? defaultLoopCount;
45
- if (!image.gifLoopCount) {
46
- image.gifLoopCount = Infinity;
47
- }
48
- }
49
- catch {
50
- image.error = true;
51
- }
52
- image.loading = false;
53
- }
54
35
  export async function downloadSvgImage(image) {
55
36
  if (image.type !== "svg") {
56
37
  await loadImage(image);
package/browser/index.js CHANGED
@@ -1,6 +1,4 @@
1
- import { downloadSvgImage, loadGifImage, loadImage } from "./Utils.js";
2
- import { ImageDrawer } from "./ImageDrawer.js";
3
- import { ImagePreloaderPlugin } from "./ImagePreloader.js";
1
+ import { downloadSvgImage, loadImage } from "./Utils.js";
4
2
  import { errorPrefix } from "@tsparticles/engine";
5
3
  const extLength = 3;
6
4
  function addLoadImageToEngine(engine) {
@@ -31,6 +29,7 @@ function addLoadImageToEngine(engine) {
31
29
  engine.images.push(image);
32
30
  let imageFunc;
33
31
  if (data.gif) {
32
+ const { loadGifImage } = await import("./GifUtils/Utils.js");
34
33
  imageFunc = loadGifImage;
35
34
  }
36
35
  else {
@@ -45,6 +44,7 @@ function addLoadImageToEngine(engine) {
45
44
  }
46
45
  export async function loadImageShape(engine, refresh = true) {
47
46
  addLoadImageToEngine(engine);
47
+ const { ImagePreloaderPlugin } = await import("./ImagePreloader.js"), { ImageDrawer } = await import("./ImageDrawer.js");
48
48
  const preloader = new ImagePreloaderPlugin(engine);
49
49
  await engine.addPlugin(preloader, refresh);
50
50
  await engine.addShape(["image", "images"], new ImageDrawer(engine), refresh);
@@ -30,12 +30,13 @@ class ByteStream {
30
30
  return blockString;
31
31
  }
32
32
  readSubBlocksBin() {
33
- let size = 0, len = 0;
33
+ let size = this.data[this.pos], len = 0;
34
34
  const emptySize = 0, increment = 1;
35
35
  for (let offset = 0; size !== emptySize; offset += size + increment, size = this.data[this.pos + offset]) {
36
36
  len += size;
37
37
  }
38
38
  const blockData = new Uint8Array(len);
39
+ size = this.data[this.pos++];
39
40
  for (let i = 0; size !== emptySize; size = this.data[this.pos++]) {
40
41
  for (let count = size; --count >= emptySize; blockData[i++] = this.data[this.pos++]) {
41
42
  }
@@ -1,8 +1,35 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decodeGIF = exports.getGIFLoopAmount = void 0;
26
+ exports.loadGifImage = exports.drawGif = exports.decodeGIF = exports.getGIFLoopAmount = void 0;
4
27
  const Constants_js_1 = require("./Constants.js");
5
28
  const ByteStream_js_1 = require("./ByteStream.js");
29
+ const origin = {
30
+ x: 0,
31
+ y: 0,
32
+ }, defaultFrame = 0, half = 0.5, initialTime = 0, firstIndex = 0, defaultLoopCount = 0;
6
33
  function parseColorTable(byteStream, count) {
7
34
  const colors = [];
8
35
  for (let i = 0; i < count; i++) {
@@ -334,3 +361,97 @@ async function decodeGIF(gifURL, progressCallback, avgAlpha) {
334
361
  }
335
362
  }
336
363
  exports.decodeGIF = decodeGIF;
364
+ function drawGif(data) {
365
+ const { context, radius, particle, delta } = data, image = particle.image;
366
+ if (!image?.gifData || !image.gif) {
367
+ return;
368
+ }
369
+ const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
370
+ if (!offscreenContext) {
371
+ throw new Error("could not create offscreen canvas context");
372
+ }
373
+ offscreenContext.imageSmoothingQuality = "low";
374
+ offscreenContext.imageSmoothingEnabled = false;
375
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
376
+ if (particle.gifLoopCount === undefined) {
377
+ particle.gifLoopCount = image.gifLoopCount ?? defaultLoopCount;
378
+ }
379
+ let frameIndex = particle.gifFrame ?? defaultFrame;
380
+ const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
381
+ if (particle.gifTime === undefined) {
382
+ particle.gifTime = initialTime;
383
+ }
384
+ if (!frame.bitmap) {
385
+ return;
386
+ }
387
+ context.scale(radius / image.gifData.width, radius / image.gifData.height);
388
+ switch (frame.disposalMethod) {
389
+ case 4:
390
+ case 5:
391
+ case 6:
392
+ case 7:
393
+ case 0:
394
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
395
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
396
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
397
+ break;
398
+ case 1:
399
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
400
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
401
+ break;
402
+ case 2:
403
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
404
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
405
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
406
+ if (!image.gifData.globalColorTable.length) {
407
+ offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
408
+ }
409
+ else {
410
+ offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y);
411
+ }
412
+ break;
413
+ case 3:
414
+ {
415
+ const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
416
+ offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
417
+ context.drawImage(offscreenCanvas, pos.x, pos.y);
418
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
419
+ offscreenContext.putImageData(previousImageData, origin.x, origin.y);
420
+ }
421
+ break;
422
+ }
423
+ particle.gifTime += delta.value;
424
+ if (particle.gifTime > frame.delayTime) {
425
+ particle.gifTime -= frame.delayTime;
426
+ if (++frameIndex >= image.gifData.frames.length) {
427
+ if (--particle.gifLoopCount <= defaultLoopCount) {
428
+ return;
429
+ }
430
+ frameIndex = firstIndex;
431
+ offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
432
+ }
433
+ particle.gifFrame = frameIndex;
434
+ }
435
+ context.scale(image.gifData.width / radius, image.gifData.height / radius);
436
+ }
437
+ exports.drawGif = drawGif;
438
+ async function loadGifImage(image) {
439
+ if (image.type !== "gif") {
440
+ const { loadImage } = await Promise.resolve().then(() => __importStar(require("../Utils.js")));
441
+ await loadImage(image);
442
+ return;
443
+ }
444
+ image.loading = true;
445
+ try {
446
+ image.gifData = await decodeGIF(image.source);
447
+ image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? defaultLoopCount;
448
+ if (!image.gifLoopCount) {
449
+ image.gifLoopCount = Infinity;
450
+ }
451
+ }
452
+ catch {
453
+ image.error = true;
454
+ }
455
+ image.loading = false;
456
+ }
457
+ exports.loadGifImage = loadGifImage;