@tsparticles/shape-image 4.0.0-alpha.5 → 4.0.0-beta.0

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 (60) hide show
  1. package/375.min.js +1 -0
  2. package/550.min.js +1 -0
  3. package/682.min.js +1 -0
  4. package/814.min.js +1 -0
  5. package/browser/GifUtils/ByteStream.js +3 -1
  6. package/browser/GifUtils/Utils.js +36 -42
  7. package/browser/ImageDrawer.js +29 -35
  8. package/browser/ImagePreloader.js +7 -5
  9. package/browser/ImagePreloaderInstance.js +11 -0
  10. package/browser/Options/Classes/Preload.js +6 -0
  11. package/browser/Utils.js +7 -6
  12. package/browser/index.js +27 -13
  13. package/cjs/GifUtils/ByteStream.js +3 -1
  14. package/cjs/GifUtils/Utils.js +36 -42
  15. package/cjs/ImageDrawer.js +29 -35
  16. package/cjs/ImagePreloader.js +7 -5
  17. package/cjs/ImagePreloaderInstance.js +11 -0
  18. package/cjs/Options/Classes/Preload.js +6 -0
  19. package/cjs/Utils.js +7 -6
  20. package/cjs/index.js +27 -13
  21. package/dist_browser_GifUtils_Utils_js.js +6 -16
  22. package/dist_browser_ImageDrawer_js.js +2 -2
  23. package/dist_browser_ImagePreloaderInstance_js.js +30 -0
  24. package/dist_browser_ImagePreloader_js.js +3 -3
  25. package/esm/GifUtils/ByteStream.js +3 -1
  26. package/esm/GifUtils/Utils.js +36 -42
  27. package/esm/ImageDrawer.js +29 -35
  28. package/esm/ImagePreloader.js +7 -5
  29. package/esm/ImagePreloaderInstance.js +11 -0
  30. package/esm/Options/Classes/Preload.js +6 -0
  31. package/esm/Utils.js +7 -6
  32. package/esm/index.js +27 -13
  33. package/package.json +2 -2
  34. package/report.html +3 -3
  35. package/tsparticles.shape.image.js +40 -18
  36. package/tsparticles.shape.image.min.js +2 -2
  37. package/types/GifUtils/Utils.d.ts +4 -4
  38. package/types/ImageDrawer.d.ts +1 -3
  39. package/types/ImagePreloader.d.ts +5 -4
  40. package/types/ImagePreloaderInstance.d.ts +8 -0
  41. package/types/Utils.d.ts +1 -0
  42. package/types/types.d.ts +3 -2
  43. package/umd/GifUtils/ByteStream.js +3 -1
  44. package/umd/GifUtils/Utils.js +37 -43
  45. package/umd/ImageDrawer.js +28 -34
  46. package/umd/ImagePreloader.js +41 -5
  47. package/umd/ImagePreloaderInstance.js +25 -0
  48. package/umd/Options/Classes/Preload.js +6 -0
  49. package/umd/Utils.js +8 -6
  50. package/umd/index.js +28 -14
  51. package/324.min.js +0 -2
  52. package/324.min.js.LICENSE.txt +0 -1
  53. package/337.min.js +0 -2
  54. package/337.min.js.LICENSE.txt +0 -1
  55. package/413.min.js +0 -2
  56. package/413.min.js.LICENSE.txt +0 -1
  57. package/72.min.js +0 -2
  58. package/72.min.js.LICENSE.txt +0 -1
  59. package/dist_browser_Utils_js.js +0 -30
  60. package/tsparticles.shape.image.min.js.LICENSE.txt +0 -1
package/375.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[375],{375(e,i,t){t.d(i,{ImagePreloaderPlugin:()=>a});var s=t(303);class r{gif;height;name;replaceColor;src;width;constructor(){this.src="",this.gif=!1}load(e){(0,s.isNull)(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 a{id="image-preloader";_engine;constructor(e){this._engine=e}async getPlugin(e){let{ImagePreloaderInstance:i}=await t.e(550).then(t.bind(t,550));return new i(this._engine,e)}loadOptions(e,i,t){if(!t?.preload)return;i.preload??=[];let s=i.preload;for(let e of t.preload){let i=s.find(i=>i.name===e.name||i.src===e.src);if(i)i.load(e);else{let i=new r;i.load(e),s.push(i)}}}needsPlugin(){return!0}}}}]);
package/550.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[550],{550(e,s,t){t.d(s,{ImagePreloaderInstance:()=>i});class i{_container;_engine;constructor(e,s){this._engine=e,this._container=s}destroy(){this._engine.images?.delete(this._container)}}}}]);
package/682.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[682,814],{814(e,t,a){a.d(t,{zS:()=>w,loadGifImage:()=>x});var i,o,n,r,l=a(183),s=a(303);let h=[0,4,2,1],g=[8,8,4,2];class d{data;pos;constructor(e){this.pos=0,this.data=new Uint8ClampedArray(e)}getString(e){let t=this.data.slice(this.pos,this.pos+e);return this.pos+=t.length,t.reduce((e,t)=>e+String.fromCharCode(t),"")}nextByte(){return this.data[this.pos++]}nextTwoBytes(){return this.pos+=2,this.data[this.pos-2]+(this.data[this.pos-1]<<8)}readSubBlocks(){let e="",t;do{t=this.data[this.pos++];for(let a=t;--a>=0;e+=String.fromCharCode(this.data[this.pos++]));}while(0!==t)return e}readSubBlocksBin(){let e=this.data[this.pos],t=0;for(let a=0;0!==e;a+=e+1,e=this.data[this.pos+a])t+=e;let a=new Uint8Array(t);e=this.data[this.pos++];for(let t=0;0!==e;e=this.data[this.pos++])for(let i=e;--i>=0;a[t++]=this.data[this.pos++]);return a}skipSubBlocks(){for(;0!==this.data[this.pos];this.pos+=this.data[this.pos]+1);this.pos++}}function f(e,t){let a=[];for(let i=0;i<t;i++)a.push({r:e.data[e.pos],g:e.data[e.pos+1],b:e.data[e.pos+2]}),e.pos+=3;return a}function c(e,t,a){let i=t>>>3,o=7&t;return(e[i]+(e[i+1]<<8)+(e[i+2]<<16)&(1<<a)-1<<o)>>>o}async function p(e,t,a,i,o,n,r){let l=t.frames[i(!0)];l.left=e.nextTwoBytes(),l.top=e.nextTwoBytes(),l.width=e.nextTwoBytes(),l.height=e.nextTwoBytes();let s=e.nextByte(),d=(128&s)==128;l.sortFlag=(32&s)==32,l.reserved=(24&s)>>>3,d&&(l.localColorTable=f(e,1<<(7&s)+1));let p=e=>{let{r:i,g:n,b:r}=(d?l.localColorTable:t.globalColorTable)[e];return e!==o(null)?{r:i,g:n,b:r,a:255}:{r:i,g:n,b:r,a:a?Math.trunc((i+n+r)/3):0}},m=(()=>{try{return new ImageData(l.width,l.height,n)}catch(e){if(e instanceof DOMException&&"IndexSizeError"===e.name)return null;throw e}})();if(null==m)throw EvalError("GIF frame size is to large");let u=e.nextByte(),w=e.readSubBlocksBin(),x=1<<u;if((64&s)==64){for(let a=0,o=u+1,n=0,s=[[0]],d=0;d<4;d++){if(h[d]<l.height){let e=0,t=0,i=!1;for(;!i;){let r=a;if(a=c(w,n,o),n+=o+1,a===x){o=u+1,s.length=x+2;for(let e=0;e<s.length;e++)s[e]=e<x?[e]:[]}else{for(let i of(a>=s.length?s.push(s[r].concat(s[r][0])):r!==x&&s.push(s[r].concat(s[a][0])),s[a])){let{r:a,g:o,b:n,a:r}=p(i);m.data.set([a,o,n,r],h[d]*l.width+g[d]*t+e%(4*l.width)),e+=4}s.length===1<<o&&o<12&&o++}e===4*l.width*(t+1)&&(t++,h[d]+g[d]*t>=l.height&&(i=!0))}}r?.(e.pos/(e.data.length-1),i(!1)+1,m,{x:l.left,y:l.top},{width:t.width,height:t.height})}l.image=m,l.bitmap=await createImageBitmap(m)}else{let a=0,o=u+1,n=0,s=-4,h=[[0]];for(;;){let e=a;if(a=c(w,n,o),n+=o,a===x){o=u+1,h.length=x+2;for(let e=0;e<h.length;e++)h[e]=e<x?[e]:[]}else{if(a===x+1)break;for(let t of(a>=h.length?h.push(h[e].concat(h[e][0])):e!==x&&h.push(h[e].concat(h[a][0])),h[a])){let{r:e,g:a,b:i,a:o}=p(t);s+=4,m.data.set([e,a,i,o],s)}h.length>=1<<o&&o<12&&o++}}l.image=m,l.bitmap=await createImageBitmap(m),r?.((e.pos+1)/e.data.length,i(!1)+1,l.image,{x:l.left,y:l.top},{width:t.width,height:t.height})}}async function m(e,t,a,i,o,n,l){switch(e.nextByte()){case r.EndOfFile:return!0;case r.Image:await p(e,t,a,i,o,n,l);break;case r.Extension:switch(e.nextByte()){case r.GraphicsControlExtension:{let a=t.frames[i(!1)];e.pos++;let n=e.nextByte();a.GCreserved=(224&n)>>>5,a.disposalMethod=(28&n)>>>2,a.userInputDelayFlag=(2&n)==2,a.delayTime=10*e.nextTwoBytes();let r=e.nextByte();(1&n)==1&&o(r),e.pos++;break}case r.ApplicationExtension:{e.pos++;let a={identifier:e.getString(8),authenticationCode:e.getString(3),data:e.readSubBlocksBin()};t.applicationExtensions.push(a);break}case r.CommentExtension:t.comments.push([i(!1),e.readSubBlocks()]);break;case r.PlainTextExtension:if(0===t.globalColorTable.length)throw EvalError("plain text extension without global color table");e.pos++,t.frames[i(!1)].plainTextData={left:e.nextTwoBytes(),top:e.nextTwoBytes(),width:e.nextTwoBytes(),height:e.nextTwoBytes(),charSize:{width:e.nextTwoBytes(),height:e.nextTwoBytes()},foregroundColor:e.nextByte(),backgroundColor:e.nextByte(),text:e.readSubBlocks()};break;default:e.skipSubBlocks()}break;default:throw EvalError("undefined block found")}return!1}async function u(e,t,a,i){i??=!1;let o=await fetch(e);if(!o.ok&&404===o.status)throw EvalError("file not found");let r=await o.arrayBuffer(),l={width:0,height:0,totalTime:0,colorRes:0,pixelAspectRatio:0,frames:[],sortFlag:!1,globalColorTable:[],backgroundImage:new ImageData(1,1,t),comments:[],applicationExtensions:[]},s=new d(new Uint8ClampedArray(r));if("GIF89a"!==s.getString(6))throw Error("not a supported GIF file");l.width=s.nextTwoBytes(),l.height=s.nextTwoBytes();let h=s.nextByte(),g=(128&h)==128;l.colorRes=(112&h)>>>4,l.sortFlag=(8&h)==8;let c=s.nextByte();l.pixelAspectRatio=s.nextByte(),0!==l.pixelAspectRatio&&(l.pixelAspectRatio=(l.pixelAspectRatio+15)/64),g&&(l.globalColorTable=f(s,1<<(7&h)+1));let p=(()=>{try{return new ImageData(l.width,l.height,t)}catch(e){if(e instanceof DOMException&&"IndexSizeError"===e.name)return null;throw e}})();if(null==p)throw Error("GIF frame size is to large");let{r:u,g:w,b:x}=l.globalColorTable[c];p.data.set(g?[u,w,x,255]:[0,0,0,0]);for(let e=4;e<p.data.length;e*=2)p.data.copyWithin(e,0,e);l.backgroundImage=p;let y=-1,b=!0,C=-1,I=e=>(e&&(b=!0),y),B=e=>(null!=e&&(C=e),C);try{do b&&(l.frames.push({left:0,top:0,width:0,height:0,disposalMethod:n.Replace,image:new ImageData(1,1,t),plainTextData:null,userInputDelayFlag:!1,delayTime:0,sortFlag:!1,localColorTable:[],reserved:0,GCreserved:0}),y++,C=-1,b=!1);while(!await m(s,l,i,I,B,t,a))for(let e of(l.frames.length--,l.frames)){if(e.userInputDelayFlag&&0===e.delayTime){l.totalTime=1/0;break}l.totalTime+=e.delayTime}return l}catch(e){if(e instanceof EvalError)throw Error(`error while parsing frame ${y.toString()} "${e.message}"`,{cause:e});throw e}}function w(e,t){let{context:a,radius:i,particle:o,delta:r}=e,l=o.image;if(!l?.gifData||!l.gif)return;let h=new OffscreenCanvas(l.gifData.width,l.gifData.height),g=h.getContext("2d",t);if(!g)throw Error("could not create offscreen canvas context");g.imageSmoothingQuality="low",g.imageSmoothingEnabled=!1,g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height),o.gifLoopCount??=l.gifLoopCount??0;let d=o.gifFrame??0,f={x:-l.gifData.width*s.half,y:-l.gifData.height*s.half},c=l.gifData.frames[d];if(o.gifTime??=0,c.bitmap){switch(a.scale(i/l.gifData.width,i/l.gifData.height),c.disposalMethod){case n.UndefinedA:case n.UndefinedB:case n.UndefinedC:case n.UndefinedD:case n.Replace:g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y),g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height);break;case n.Combine:g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y);break;case n.RestoreBackground:g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y),g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height),l.gifData.globalColorTable.length?g.putImageData(l.gifData.backgroundImage,f.x,f.y):g.putImageData(l.gifData.frames[0].image,f.x+c.left,f.y+c.top);break;case n.RestorePrevious:{let e=g.getImageData(s.originPoint.x,s.originPoint.y,h.width,h.height);g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y),g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height),g.putImageData(e,s.originPoint.x,s.originPoint.y)}}if(o.gifTime+=r.value,o.gifTime>c.delayTime){if(o.gifTime-=c.delayTime,++d>=l.gifData.frames.length){if(--o.gifLoopCount<=0)return;d=0,g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height)}o.gifFrame=d}a.scale(l.gifData.width/i,l.gifData.height/i)}}async function x(e,t){if("gif"!==e.type)return void await (0,l.loadImage)(e);e.loading=!0;try{e.gifData=await u(e.source,t),e.gifLoopCount=function(e){for(let t of e.applicationExtensions)if(t.identifier+t.authenticationCode==="NETSCAPE2.0")return t.data[1]+(t.data[2]<<8);return NaN}(e.gifData),e.gifLoopCount||(e.gifLoopCount=1/0)}catch{e.error=!0}e.loading=!1}(i=n||(n={}))[i.Replace=0]="Replace",i[i.Combine=1]="Combine",i[i.RestoreBackground=2]="RestoreBackground",i[i.RestorePrevious=3]="RestorePrevious",i[i.UndefinedA=4]="UndefinedA",i[i.UndefinedB=5]="UndefinedB",i[i.UndefinedC=6]="UndefinedC",i[i.UndefinedD=7]="UndefinedD",(o=r||(r={}))[o.Extension=33]="Extension",o[o.ApplicationExtension=255]="ApplicationExtension",o[o.GraphicsControlExtension=249]="GraphicsControlExtension",o[o.PlainTextExtension=1]="PlainTextExtension",o[o.CommentExtension=254]="CommentExtension",o[o.Image=44]="Image",o[o.EndOfFile=59]="EndOfFile"},682(e,t,a){a.d(t,{ImageDrawer:()=>r});var i=a(303),o=a(183),n=a(814);class r{_engine;constructor(e){this._engine=e}draw(e){let{context:t,radius:a,particle:o,opacity:r}=e,l=o.image,s=l?.element;if(l){if(t.globalAlpha=r,l.gif&&l.gifData)(0,n.zS)(e,o.container.canvas.settings);else if(s){let e=l.ratio,o={x:-a,y:-a},n=a*i.double;t.drawImage(s,o.x,o.y,n,n/e)}t.globalAlpha=i.defaultAlpha}}getSidesCount(){return 12}async init(e){let t=e.actualOptions;if(!t.preload||!this._engine.loadImage)return;let a=[];for(let i of t.preload)a.push(this._engine.loadImage(e,i));await Promise.all(a)}loadShape(e){let{container:t}=e;if(!e.shape||!o.z.includes(e.shape))return;let a=e.shapeData;if(!a)return;let i=this._engine.getImages?.(t);i?.find(e=>e.name===a.name||e.source===a.src)||this.loadImageShape(t,a).then(()=>{this.loadShape(e)})}particleInit(e,t){if("image"!==t.shape&&"images"!==t.shape)return;let a=this._engine.getImages?.(e),n=t.shapeData;if(!n)return;let r=t.getFillColor(),l=a?.find(e=>e.name===n.name||e.source===n.src);if(!l)return;let s=n.replaceColor;l.loading?setTimeout(()=>{this.particleInit(e,t)}):(async()=>{let a;(a=l.svgData&&r?await (0,o.d)(l,n,r,t,e.hdr):{color:r,data:l,element:l.element,gif:l.gif,gifData:l.gifData,gifLoopCount:l.gifLoopCount,loaded:!0,ratio:n.width&&n.height?n.width/n.height:l.ratio??i.defaultRatio,replaceColor:s,source:n.src}).ratio||(a.ratio=1);let h=n.close??t.shapeClose;t.image=a,t.shapeClose=h})()}loadImageShape=async(e,t)=>{if(!this._engine.loadImage)throw Error("Image shape not initialized");await this._engine.loadImage(e,{gif:t.gif,name:t.name,replaceColor:t.replaceColor,src:t.src})}}}}]);
package/814.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_shape_image=this.webpackChunk_tsparticles_shape_image||[]).push([[814],{814(t,e,a){a.d(e,{zS:()=>w,loadGifImage:()=>x});var i,o,n,r,l=a(183),s=a(303);let h=[0,4,2,1],g=[8,8,4,2];class d{data;pos;constructor(t){this.pos=0,this.data=new Uint8ClampedArray(t)}getString(t){let 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;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;let a=new Uint8Array(e);t=this.data[this.pos++];for(let e=0;0!==t;t=this.data[this.pos++])for(let i=t;--i>=0;a[e++]=this.data[this.pos++]);return a}skipSubBlocks(){for(;0!==this.data[this.pos];this.pos+=this.data[this.pos]+1);this.pos++}}function f(t,e){let a=[];for(let i=0;i<e;i++)a.push({r:t.data[t.pos],g:t.data[t.pos+1],b:t.data[t.pos+2]}),t.pos+=3;return a}function c(t,e,a){let i=e>>>3,o=7&e;return(t[i]+(t[i+1]<<8)+(t[i+2]<<16)&(1<<a)-1<<o)>>>o}async function p(t,e,a,i,o,n,r){let l=e.frames[i(!0)];l.left=t.nextTwoBytes(),l.top=t.nextTwoBytes(),l.width=t.nextTwoBytes(),l.height=t.nextTwoBytes();let s=t.nextByte(),d=(128&s)==128;l.sortFlag=(32&s)==32,l.reserved=(24&s)>>>3,d&&(l.localColorTable=f(t,1<<(7&s)+1));let p=t=>{let{r:i,g:n,b:r}=(d?l.localColorTable:e.globalColorTable)[t];return t!==o(null)?{r:i,g:n,b:r,a:255}:{r:i,g:n,b:r,a:a?Math.trunc((i+n+r)/3):0}},u=(()=>{try{return new ImageData(l.width,l.height,n)}catch(t){if(t instanceof DOMException&&"IndexSizeError"===t.name)return null;throw t}})();if(null==u)throw EvalError("GIF frame size is to large");let m=t.nextByte(),w=t.readSubBlocksBin(),x=1<<m;if((64&s)==64){for(let a=0,o=m+1,n=0,s=[[0]],d=0;d<4;d++){if(h[d]<l.height){let t=0,e=0,i=!1;for(;!i;){let r=a;if(a=c(w,n,o),n+=o+1,a===x){o=m+1,s.length=x+2;for(let t=0;t<s.length;t++)s[t]=t<x?[t]:[]}else{for(let i of(a>=s.length?s.push(s[r].concat(s[r][0])):r!==x&&s.push(s[r].concat(s[a][0])),s[a])){let{r:a,g:o,b:n,a:r}=p(i);u.data.set([a,o,n,r],h[d]*l.width+g[d]*e+t%(4*l.width)),t+=4}s.length===1<<o&&o<12&&o++}t===4*l.width*(e+1)&&(e++,h[d]+g[d]*e>=l.height&&(i=!0))}}r?.(t.pos/(t.data.length-1),i(!1)+1,u,{x:l.left,y:l.top},{width:e.width,height:e.height})}l.image=u,l.bitmap=await createImageBitmap(u)}else{let a=0,o=m+1,n=0,s=-4,h=[[0]];for(;;){let t=a;if(a=c(w,n,o),n+=o,a===x){o=m+1,h.length=x+2;for(let t=0;t<h.length;t++)h[t]=t<x?[t]:[]}else{if(a===x+1)break;for(let e of(a>=h.length?h.push(h[t].concat(h[t][0])):t!==x&&h.push(h[t].concat(h[a][0])),h[a])){let{r:t,g:a,b:i,a:o}=p(e);s+=4,u.data.set([t,a,i,o],s)}h.length>=1<<o&&o<12&&o++}}l.image=u,l.bitmap=await createImageBitmap(u),r?.((t.pos+1)/t.data.length,i(!1)+1,l.image,{x:l.left,y:l.top},{width:e.width,height:e.height})}}async function u(t,e,a,i,o,n,l){switch(t.nextByte()){case r.EndOfFile:return!0;case r.Image:await p(t,e,a,i,o,n,l);break;case r.Extension:switch(t.nextByte()){case r.GraphicsControlExtension:{let a=e.frames[i(!1)];t.pos++;let n=t.nextByte();a.GCreserved=(224&n)>>>5,a.disposalMethod=(28&n)>>>2,a.userInputDelayFlag=(2&n)==2,a.delayTime=10*t.nextTwoBytes();let r=t.nextByte();(1&n)==1&&o(r),t.pos++;break}case r.ApplicationExtension:{t.pos++;let a={identifier:t.getString(8),authenticationCode:t.getString(3),data:t.readSubBlocksBin()};e.applicationExtensions.push(a);break}case r.CommentExtension:e.comments.push([i(!1),t.readSubBlocks()]);break;case r.PlainTextExtension:if(0===e.globalColorTable.length)throw EvalError("plain text extension without global color table");t.pos++,e.frames[i(!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()}break;default:throw EvalError("undefined block found")}return!1}async function m(t,e,a,i){i??=!1;let o=await fetch(t);if(!o.ok&&404===o.status)throw EvalError("file not found");let r=await o.arrayBuffer(),l={width:0,height:0,totalTime:0,colorRes:0,pixelAspectRatio:0,frames:[],sortFlag:!1,globalColorTable:[],backgroundImage:new ImageData(1,1,e),comments:[],applicationExtensions:[]},s=new d(new Uint8ClampedArray(r));if("GIF89a"!==s.getString(6))throw Error("not a supported GIF file");l.width=s.nextTwoBytes(),l.height=s.nextTwoBytes();let h=s.nextByte(),g=(128&h)==128;l.colorRes=(112&h)>>>4,l.sortFlag=(8&h)==8;let c=s.nextByte();l.pixelAspectRatio=s.nextByte(),0!==l.pixelAspectRatio&&(l.pixelAspectRatio=(l.pixelAspectRatio+15)/64),g&&(l.globalColorTable=f(s,1<<(7&h)+1));let p=(()=>{try{return new ImageData(l.width,l.height,e)}catch(t){if(t instanceof DOMException&&"IndexSizeError"===t.name)return null;throw t}})();if(null==p)throw Error("GIF frame size is to large");let{r:m,g:w,b:x}=l.globalColorTable[c];p.data.set(g?[m,w,x,255]:[0,0,0,0]);for(let t=4;t<p.data.length;t*=2)p.data.copyWithin(t,0,t);l.backgroundImage=p;let y=-1,b=!0,B=-1,C=t=>(t&&(b=!0),y),E=t=>(null!=t&&(B=t),B);try{do b&&(l.frames.push({left:0,top:0,width:0,height:0,disposalMethod:n.Replace,image:new ImageData(1,1,e),plainTextData:null,userInputDelayFlag:!1,delayTime:0,sortFlag:!1,localColorTable:[],reserved:0,GCreserved:0}),y++,B=-1,b=!1);while(!await u(s,l,i,C,E,e,a))for(let t of(l.frames.length--,l.frames)){if(t.userInputDelayFlag&&0===t.delayTime){l.totalTime=1/0;break}l.totalTime+=t.delayTime}return l}catch(t){if(t instanceof EvalError)throw Error(`error while parsing frame ${y.toString()} "${t.message}"`,{cause:t});throw t}}function w(t,e){let{context:a,radius:i,particle:o,delta:r}=t,l=o.image;if(!l?.gifData||!l.gif)return;let h=new OffscreenCanvas(l.gifData.width,l.gifData.height),g=h.getContext("2d",e);if(!g)throw Error("could not create offscreen canvas context");g.imageSmoothingQuality="low",g.imageSmoothingEnabled=!1,g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height),o.gifLoopCount??=l.gifLoopCount??0;let d=o.gifFrame??0,f={x:-l.gifData.width*s.half,y:-l.gifData.height*s.half},c=l.gifData.frames[d];if(o.gifTime??=0,c.bitmap){switch(a.scale(i/l.gifData.width,i/l.gifData.height),c.disposalMethod){case n.UndefinedA:case n.UndefinedB:case n.UndefinedC:case n.UndefinedD:case n.Replace:g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y),g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height);break;case n.Combine:g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y);break;case n.RestoreBackground:g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y),g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height),l.gifData.globalColorTable.length?g.putImageData(l.gifData.backgroundImage,f.x,f.y):g.putImageData(l.gifData.frames[0].image,f.x+c.left,f.y+c.top);break;case n.RestorePrevious:{let t=g.getImageData(s.originPoint.x,s.originPoint.y,h.width,h.height);g.drawImage(c.bitmap,c.left,c.top),a.drawImage(h,f.x,f.y),g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height),g.putImageData(t,s.originPoint.x,s.originPoint.y)}}if(o.gifTime+=r.value,o.gifTime>c.delayTime){if(o.gifTime-=c.delayTime,++d>=l.gifData.frames.length){if(--o.gifLoopCount<=0)return;d=0,g.clearRect(s.originPoint.x,s.originPoint.y,h.width,h.height)}o.gifFrame=d}a.scale(l.gifData.width/i,l.gifData.height/i)}}async function x(t,e){if("gif"!==t.type)return void await (0,l.loadImage)(t);t.loading=!0;try{t.gifData=await m(t.source,e),t.gifLoopCount=function(t){for(let e of t.applicationExtensions)if(e.identifier+e.authenticationCode==="NETSCAPE2.0")return e.data[1]+(e.data[2]<<8);return NaN}(t.gifData),t.gifLoopCount||(t.gifLoopCount=1/0)}catch{t.error=!0}t.loading=!1}(i=n||(n={}))[i.Replace=0]="Replace",i[i.Combine=1]="Combine",i[i.RestoreBackground=2]="RestoreBackground",i[i.RestorePrevious=3]="RestorePrevious",i[i.UndefinedA=4]="UndefinedA",i[i.UndefinedB=5]="UndefinedB",i[i.UndefinedC=6]="UndefinedC",i[i.UndefinedD=7]="UndefinedD",(o=r||(r={}))[o.Extension=33]="Extension",o[o.ApplicationExtension=255]="ApplicationExtension",o[o.GraphicsControlExtension=249]="GraphicsControlExtension",o[o.PlainTextExtension=1]="PlainTextExtension",o[o.CommentExtension=254]="CommentExtension",o[o.Image=44]="Image",o[o.EndOfFile=59]="EndOfFile"}}]);
@@ -1,4 +1,6 @@
1
1
  export class ByteStream {
2
+ data;
3
+ pos;
2
4
  constructor(bytes) {
3
5
  this.pos = 0;
4
6
  this.data = new Uint8ClampedArray(bytes);
@@ -17,7 +19,7 @@ export class ByteStream {
17
19
  return this.data[this.pos - increment] + (this.data[this.pos - previous] << shift);
18
20
  }
19
21
  readSubBlocks() {
20
- let blockString = "", size = 0;
22
+ let blockString = "", size;
21
23
  const minCount = 0, emptySize = 0;
22
24
  do {
23
25
  size = this.data[this.pos++];
@@ -1,13 +1,10 @@
1
- import { half } from "@tsparticles/engine";
2
1
  import { loadImage } from "../Utils.js";
2
+ import { half, originPoint } from "@tsparticles/engine";
3
3
  import { InterlaceOffsets, InterlaceSteps } from "./Constants.js";
4
4
  import { ByteStream } from "./ByteStream.js";
5
5
  import { DisposalMethod } from "./Enums/DisposalMethod.js";
6
6
  import { GIFDataHeaders } from "./Types/GIFDataHeaders.js";
7
- const origin = {
8
- x: 0,
9
- y: 0,
10
- }, defaultFrame = 0, initialTime = 0, firstIndex = 0, defaultLoopCount = 0;
7
+ const defaultFrame = 0, initialTime = 0, firstIndex = 0, defaultLoopCount = 0;
11
8
  function parseColorTable(byteStream, count) {
12
9
  const colors = [];
13
10
  for (let i = 0; i < count; i++) {
@@ -77,7 +74,13 @@ function parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyInde
77
74
  break;
78
75
  }
79
76
  }
80
- async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
77
+ function readBits(imageData, pos, len) {
78
+ const bytePos = pos >>> 3, bitPos = pos & 7;
79
+ return (((imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16)) &
80
+ (((1 << len) - 1) << bitPos)) >>>
81
+ bitPos);
82
+ }
83
+ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, canvasSettings, progressCallback) {
81
84
  const frame = gif.frames[getFrameIndex(true)];
82
85
  frame.left = byteStream.nextTwoBytes();
83
86
  frame.top = byteStream.nextTwoBytes();
@@ -96,10 +99,9 @@ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTran
96
99
  return { r, g, b, a: 255 };
97
100
  }
98
101
  return { r, g, b, a: avgAlpha ? Math.trunc((r + g + b) / 3) : 0 };
99
- };
100
- const image = (() => {
102
+ }, image = (() => {
101
103
  try {
102
- return new ImageData(frame.width, frame.height, { colorSpace: "srgb" });
104
+ return new ImageData(frame.width, frame.height, canvasSettings);
103
105
  }
104
106
  catch (error) {
105
107
  if (error instanceof DOMException && error.name === "IndexSizeError") {
@@ -112,19 +114,13 @@ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTran
112
114
  throw new EvalError("GIF frame size is to large");
113
115
  }
114
116
  const minCodeSize = byteStream.nextByte(), imageData = byteStream.readSubBlocksBin(), clearCode = 1 << minCodeSize;
115
- const readBits = (pos, len) => {
116
- const bytePos = pos >>> 3, bitPos = pos & 7;
117
- return (((imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16)) &
118
- (((1 << len) - 1) << bitPos)) >>>
119
- bitPos);
120
- };
121
117
  if (interlacedFlag) {
122
118
  for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pass = 0; pass < 4; pass++) {
123
119
  if (InterlaceOffsets[pass] < frame.height) {
124
120
  let pixelPos = 0, lineIndex = 0, exit = false;
125
121
  while (!exit) {
126
122
  const last = code;
127
- code = readBits(pos, size);
123
+ code = readBits(imageData, pos, size);
128
124
  pos += size + 1;
129
125
  if (code === clearCode) {
130
126
  size = minCodeSize + 1;
@@ -169,7 +165,7 @@ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTran
169
165
  const dic = [[0]];
170
166
  for (;;) {
171
167
  const last = code;
172
- code = readBits(pos, size);
168
+ code = readBits(imageData, pos, size);
173
169
  pos += size;
174
170
  if (code === clearCode) {
175
171
  size = minCodeSize + 1;
@@ -190,8 +186,8 @@ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTran
190
186
  }
191
187
  for (const item of dic[code]) {
192
188
  const { r, g, b, a } = getColor(item);
193
- image.data.set([r, g, b, a], pixelPos);
194
189
  pixelPos += 4;
190
+ image.data.set([r, g, b, a], pixelPos);
195
191
  }
196
192
  if (dic.length >= 1 << size && size < 0xc) {
197
193
  size++;
@@ -203,12 +199,12 @@ async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTran
203
199
  progressCallback?.((byteStream.pos + 1) / byteStream.data.length, getFrameIndex(false) + 1, frame.image, { x: frame.left, y: frame.top }, { width: gif.width, height: gif.height });
204
200
  }
205
201
  }
206
- async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
202
+ async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, canvasSettings, progressCallback) {
207
203
  switch (byteStream.nextByte()) {
208
204
  case GIFDataHeaders.EndOfFile:
209
205
  return true;
210
206
  case GIFDataHeaders.Image:
211
- await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback);
207
+ await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, canvasSettings, progressCallback);
212
208
  break;
213
209
  case GIFDataHeaders.Extension:
214
210
  parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex);
@@ -225,16 +221,15 @@ export function getGIFLoopAmount(gif) {
225
221
  }
226
222
  return extension.data[1] + (extension.data[2] << 8);
227
223
  }
228
- return NaN;
224
+ return Number.NaN;
229
225
  }
230
- export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
226
+ export async function decodeGIF(gifURL, canvasSettings, progressCallback, avgAlpha) {
231
227
  avgAlpha ??= false;
232
228
  const res = await fetch(gifURL);
233
229
  if (!res.ok && res.status === 404) {
234
230
  throw new EvalError("file not found");
235
231
  }
236
- const buffer = await res.arrayBuffer();
237
- const gif = {
232
+ const buffer = await res.arrayBuffer(), gif = {
238
233
  width: 0,
239
234
  height: 0,
240
235
  totalTime: 0,
@@ -243,7 +238,7 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
243
238
  frames: [],
244
239
  sortFlag: false,
245
240
  globalColorTable: [],
246
- backgroundImage: new ImageData(1, 1, { colorSpace: "srgb" }),
241
+ backgroundImage: new ImageData(1, 1, canvasSettings),
247
242
  comments: [],
248
243
  applicationExtensions: [],
249
244
  }, byteStream = new ByteStream(new Uint8ClampedArray(buffer));
@@ -265,7 +260,7 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
265
260
  }
266
261
  const backgroundImage = (() => {
267
262
  try {
268
- return new ImageData(gif.width, gif.height, { colorSpace: "srgb" });
263
+ return new ImageData(gif.width, gif.height, canvasSettings);
269
264
  }
270
265
  catch (error) {
271
266
  if (error instanceof DOMException && error.name === "IndexSizeError") {
@@ -289,8 +284,7 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
289
284
  incrementFrameIndex = true;
290
285
  }
291
286
  return frameIndex;
292
- };
293
- const getTransparencyIndex = (newValue) => {
287
+ }, getTransparencyIndex = (newValue) => {
294
288
  if (newValue != null) {
295
289
  transparencyIndex = newValue;
296
290
  }
@@ -305,7 +299,7 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
305
299
  width: 0,
306
300
  height: 0,
307
301
  disposalMethod: DisposalMethod.Replace,
308
- image: new ImageData(1, 1, { colorSpace: "srgb" }),
302
+ image: new ImageData(1, 1, canvasSettings),
309
303
  plainTextData: null,
310
304
  userInputDelayFlag: false,
311
305
  delayTime: 0,
@@ -318,7 +312,7 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
318
312
  transparencyIndex = -1;
319
313
  incrementFrameIndex = false;
320
314
  }
321
- } while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, progressCallback)));
315
+ } while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, canvasSettings, progressCallback)));
322
316
  gif.frames.length--;
323
317
  for (const frame of gif.frames) {
324
318
  if (frame.userInputDelayFlag && frame.delayTime === 0) {
@@ -331,23 +325,23 @@ export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
331
325
  }
332
326
  catch (error) {
333
327
  if (error instanceof EvalError) {
334
- throw new Error(`error while parsing frame ${frameIndex.toString()} "${error.message}"`);
328
+ throw new Error(`error while parsing frame ${frameIndex.toString()} "${error.message}"`, { cause: error });
335
329
  }
336
330
  throw error;
337
331
  }
338
332
  }
339
- export function drawGif(data) {
333
+ export function drawGif(data, canvasSettings) {
340
334
  const { context, radius, particle, delta } = data, image = particle.image;
341
335
  if (!image?.gifData || !image.gif) {
342
336
  return;
343
337
  }
344
- const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d");
338
+ const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d", canvasSettings);
345
339
  if (!offscreenContext) {
346
340
  throw new Error("could not create offscreen canvas context");
347
341
  }
348
342
  offscreenContext.imageSmoothingQuality = "low";
349
343
  offscreenContext.imageSmoothingEnabled = false;
350
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
344
+ offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
351
345
  particle.gifLoopCount ??= image.gifLoopCount ?? defaultLoopCount;
352
346
  let frameIndex = particle.gifFrame ?? defaultFrame;
353
347
  const pos = { x: -image.gifData.width * half, y: -image.gifData.height * half }, frame = image.gifData.frames[frameIndex];
@@ -364,7 +358,7 @@ export function drawGif(data) {
364
358
  case DisposalMethod.Replace:
365
359
  offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
366
360
  context.drawImage(offscreenCanvas, pos.x, pos.y);
367
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
361
+ offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
368
362
  break;
369
363
  case DisposalMethod.Combine:
370
364
  offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
@@ -373,7 +367,7 @@ export function drawGif(data) {
373
367
  case DisposalMethod.RestoreBackground:
374
368
  offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
375
369
  context.drawImage(offscreenCanvas, pos.x, pos.y);
376
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
370
+ offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
377
371
  if (!image.gifData.globalColorTable.length) {
378
372
  offscreenContext.putImageData(image.gifData.frames[firstIndex].image, pos.x + frame.left, pos.y + frame.top);
379
373
  }
@@ -383,11 +377,11 @@ export function drawGif(data) {
383
377
  break;
384
378
  case DisposalMethod.RestorePrevious:
385
379
  {
386
- const previousImageData = offscreenContext.getImageData(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
380
+ const previousImageData = offscreenContext.getImageData(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
387
381
  offscreenContext.drawImage(frame.bitmap, frame.left, frame.top);
388
382
  context.drawImage(offscreenCanvas, pos.x, pos.y);
389
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
390
- offscreenContext.putImageData(previousImageData, origin.x, origin.y);
383
+ offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
384
+ offscreenContext.putImageData(previousImageData, originPoint.x, originPoint.y);
391
385
  }
392
386
  break;
393
387
  }
@@ -399,20 +393,20 @@ export function drawGif(data) {
399
393
  return;
400
394
  }
401
395
  frameIndex = firstIndex;
402
- offscreenContext.clearRect(origin.x, origin.y, offscreenCanvas.width, offscreenCanvas.height);
396
+ offscreenContext.clearRect(originPoint.x, originPoint.y, offscreenCanvas.width, offscreenCanvas.height);
403
397
  }
404
398
  particle.gifFrame = frameIndex;
405
399
  }
406
400
  context.scale(image.gifData.width / radius, image.gifData.height / radius);
407
401
  }
408
- export async function loadGifImage(image) {
402
+ export async function loadGifImage(image, canvasSettings) {
409
403
  if (image.type !== "gif") {
410
404
  await loadImage(image);
411
405
  return;
412
406
  }
413
407
  image.loading = true;
414
408
  try {
415
- image.gifData = await decodeGIF(image.source);
409
+ image.gifData = await decodeGIF(image.source, canvasSettings);
416
410
  image.gifLoopCount = getGIFLoopAmount(image.gifData);
417
411
  if (!image.gifLoopCount) {
418
412
  image.gifLoopCount = Infinity;
@@ -1,27 +1,12 @@
1
1
  import { defaultAlpha, defaultRatio, double, } from "@tsparticles/engine";
2
- import { replaceImageColor } from "./Utils.js";
2
+ import { replaceImageColor, shapeTypes } from "./Utils.js";
3
3
  import { drawGif } from "./GifUtils/Utils.js";
4
4
  const sides = 12;
5
5
  export class ImageDrawer {
6
+ _engine;
6
7
  constructor(engine) {
7
- this.validTypes = ["image", "images"];
8
- this.loadImageShape = async (imageShape) => {
9
- if (!this._engine.loadImage) {
10
- throw new Error(`Image shape not initialized`);
11
- }
12
- await this._engine.loadImage({
13
- gif: imageShape.gif,
14
- name: imageShape.name,
15
- replaceColor: imageShape.replaceColor,
16
- src: imageShape.src,
17
- });
18
- };
19
8
  this._engine = engine;
20
9
  }
21
- addImage(image) {
22
- this._engine.images ??= [];
23
- this._engine.images.push(image);
24
- }
25
10
  draw(data) {
26
11
  const { context, radius, particle, opacity } = data, image = particle.image, element = image?.element;
27
12
  if (!image) {
@@ -29,7 +14,7 @@ export class ImageDrawer {
29
14
  }
30
15
  context.globalAlpha = opacity;
31
16
  if (image.gif && image.gifData) {
32
- drawGif(data);
17
+ drawGif(data, particle.container.canvas.settings);
33
18
  }
34
19
  else if (element) {
35
20
  const ratio = image.ratio, pos = {
@@ -48,36 +33,38 @@ export class ImageDrawer {
48
33
  if (!options.preload || !this._engine.loadImage) {
49
34
  return;
50
35
  }
36
+ const promises = [];
51
37
  for (const imageData of options.preload) {
52
- await this._engine.loadImage(imageData);
38
+ promises.push(this._engine.loadImage(container, imageData));
53
39
  }
40
+ await Promise.all(promises);
54
41
  }
55
42
  loadShape(particle) {
56
- if (particle.shape !== "image" && particle.shape !== "images") {
43
+ const { container } = particle;
44
+ if (!particle.shape || !shapeTypes.includes(particle.shape)) {
57
45
  return;
58
46
  }
59
- this._engine.images ??= [];
60
47
  const imageData = particle.shapeData;
61
48
  if (!imageData) {
62
49
  return;
63
50
  }
64
- const image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src);
65
- if (!image) {
66
- void this.loadImageShape(imageData).then(() => {
67
- this.loadShape(particle);
68
- });
51
+ const images = this._engine.getImages?.(container), image = images?.find((t) => t.name === imageData.name || t.source === imageData.src);
52
+ if (image) {
53
+ return;
69
54
  }
55
+ void this.loadImageShape(container, imageData).then(() => {
56
+ this.loadShape(particle);
57
+ });
70
58
  }
71
59
  particleInit(container, particle) {
72
60
  if (particle.shape !== "image" && particle.shape !== "images") {
73
61
  return;
74
62
  }
75
- this._engine.images ??= [];
76
- const images = this._engine.images, imageData = particle.shapeData;
63
+ const images = this._engine.getImages?.(container), imageData = particle.shapeData;
77
64
  if (!imageData) {
78
65
  return;
79
66
  }
80
- const color = particle.getFillColor(), image = images.find((t) => t.name === imageData.name || t.source === imageData.src);
67
+ const color = particle.getFillColor(), image = images?.find((t) => t.name === imageData.name || t.source === imageData.src);
81
68
  if (!image) {
82
69
  return;
83
70
  }
@@ -102,9 +89,7 @@ export class ImageDrawer {
102
89
  gifData: image.gifData,
103
90
  gifLoopCount: image.gifLoopCount,
104
91
  loaded: true,
105
- ratio: imageData.width && imageData.height
106
- ? imageData.width / imageData.height
107
- : (image.ratio ?? defaultRatio),
92
+ ratio: imageData.width && imageData.height ? imageData.width / imageData.height : (image.ratio ?? defaultRatio),
108
93
  replaceColor: replaceColor,
109
94
  source: imageData.src,
110
95
  };
@@ -112,14 +97,23 @@ export class ImageDrawer {
112
97
  if (!imageRes.ratio) {
113
98
  imageRes.ratio = 1;
114
99
  }
115
- const fill = imageData.fill ?? particle.shapeFill, close = imageData.close ?? particle.shapeClose, imageShape = {
100
+ const close = imageData.close ?? particle.shapeClose, imageShape = {
116
101
  image: imageRes,
117
- fill,
118
102
  close,
119
103
  };
120
104
  particle.image = imageShape.image;
121
- particle.shapeFill = imageShape.fill;
122
105
  particle.shapeClose = imageShape.close;
123
106
  })();
124
107
  }
108
+ loadImageShape = async (container, imageShape) => {
109
+ if (!this._engine.loadImage) {
110
+ throw new Error(`Image shape not initialized`);
111
+ }
112
+ await this._engine.loadImage(container, {
113
+ gif: imageShape.gif,
114
+ name: imageShape.name,
115
+ replaceColor: imageShape.replaceColor,
116
+ src: imageShape.src,
117
+ });
118
+ };
125
119
  }
@@ -1,11 +1,13 @@
1
1
  import { Preload } from "./Options/Classes/Preload.js";
2
2
  export class ImagePreloaderPlugin {
3
- constructor() {
4
- this.id = "imagePreloader";
3
+ id = "image-preloader";
4
+ _engine;
5
+ constructor(engine) {
6
+ this._engine = engine;
5
7
  }
6
- async getPlugin() {
7
- await Promise.resolve();
8
- return {};
8
+ async getPlugin(container) {
9
+ const { ImagePreloaderInstance } = await import("./ImagePreloaderInstance.js");
10
+ return new ImagePreloaderInstance(this._engine, container);
9
11
  }
10
12
  loadOptions(_container, options, source) {
11
13
  if (!source?.preload) {
@@ -0,0 +1,11 @@
1
+ export class ImagePreloaderInstance {
2
+ _container;
3
+ _engine;
4
+ constructor(engine, container) {
5
+ this._engine = engine;
6
+ this._container = container;
7
+ }
8
+ destroy() {
9
+ this._engine.images?.delete(this._container);
10
+ }
11
+ }
@@ -1,5 +1,11 @@
1
1
  import { isNull } from "@tsparticles/engine";
2
2
  export class Preload {
3
+ gif;
4
+ height;
5
+ name;
6
+ replaceColor;
7
+ src;
8
+ width;
3
9
  constructor() {
4
10
  this.src = "";
5
11
  this.gif = false;
package/browser/Utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { getLogger, getStyleFromHsl } from "@tsparticles/engine";
2
+ export const shapeTypes = ["image", "images"];
2
3
  const stringStart = 0, defaultOpacity = 1;
3
4
  const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;
4
5
  function replaceColorSvg(imageShape, color, opacity, hdr = false) {
@@ -8,13 +9,13 @@ function replaceColorSvg(imageShape, color, opacity, hdr = false) {
8
9
  }
9
10
  const colorStyle = getStyleFromHsl(color, hdr, opacity);
10
11
  if (svgData.includes("fill")) {
11
- return svgData.replace(currentColorRegex, () => colorStyle);
12
+ return svgData.replaceAll(currentColorRegex, () => colorStyle);
12
13
  }
13
14
  const preFillIndex = svgData.indexOf(">");
14
15
  return `${svgData.substring(stringStart, preFillIndex)} fill="${colorStyle}"${svgData.substring(preFillIndex)}`;
15
16
  }
16
17
  export async function loadImage(image) {
17
- return new Promise((resolve) => {
18
+ return new Promise(resolve => {
18
19
  image.loading = true;
19
20
  const img = new Image();
20
21
  image.element = img;
@@ -39,12 +40,12 @@ export async function downloadSvgImage(image) {
39
40
  }
40
41
  image.loading = true;
41
42
  const response = await fetch(image.source);
42
- if (!response.ok) {
43
- getLogger().error("Image not found");
44
- image.error = true;
43
+ if (response.ok) {
44
+ image.svgData = await response.text();
45
45
  }
46
46
  else {
47
- image.svgData = await response.text();
47
+ getLogger().error("Image not found");
48
+ image.error = true;
48
49
  }
49
50
  image.loading = false;
50
51
  }
package/browser/index.js CHANGED
@@ -1,14 +1,25 @@
1
+ import { shapeTypes } from "./Utils.js";
1
2
  const extLength = 3;
2
3
  function addLoadImageToEngine(engine) {
3
- if (engine.loadImage) {
4
- return;
5
- }
6
- engine.loadImage = async (data) => {
4
+ engine.getImages ??= (container) => {
5
+ engine.images ??= new Map();
6
+ let images = engine.images.get(container);
7
+ if (!images) {
8
+ images = [];
9
+ engine.images.set(container, images);
10
+ }
11
+ return images;
12
+ };
13
+ engine.loadImage ??= async (container, data) => {
14
+ if (!engine.getImages) {
15
+ throw new Error("No images collection found");
16
+ }
7
17
  if (!data.name && !data.src) {
8
18
  throw new Error("No image source provided");
9
19
  }
10
- engine.images ??= [];
11
- if (engine.images.some((t) => t.name === data.name || t.source === data.src)) {
20
+ engine.images ??= new Map();
21
+ const containerImages = engine.getImages(container);
22
+ if (containerImages.some((t) => t.name === data.name || t.source === data.src)) {
12
23
  return;
13
24
  }
14
25
  try {
@@ -22,11 +33,12 @@ function addLoadImageToEngine(engine) {
22
33
  replaceColor: data.replaceColor,
23
34
  ratio: data.width && data.height ? data.width / data.height : undefined,
24
35
  };
25
- engine.images.push(image);
36
+ containerImages.push(image);
37
+ engine.images.set(container, containerImages);
26
38
  let imageFunc;
27
39
  if (data.gif) {
28
40
  const { loadGifImage } = await import("./GifUtils/Utils.js");
29
- imageFunc = loadGifImage;
41
+ imageFunc = (img) => loadGifImage(img, { colorSpace: "srgb" });
30
42
  }
31
43
  else if (data.replaceColor) {
32
44
  const { downloadSvgImage } = await import("./Utils.js");
@@ -44,12 +56,14 @@ function addLoadImageToEngine(engine) {
44
56
  };
45
57
  }
46
58
  export async function loadImageShape(engine) {
47
- engine.checkVersion("4.0.0-alpha.5");
59
+ engine.checkVersion("4.0.0-beta.0");
48
60
  await engine.register(async (e) => {
49
- const { ImageDrawer } = await import("./ImageDrawer.js"), { ImagePreloaderPlugin } = await import("./ImagePreloader.js");
61
+ const { ImagePreloaderPlugin } = await import("./ImagePreloader.js");
50
62
  addLoadImageToEngine(e);
51
- const preloader = new ImagePreloaderPlugin();
52
- e.addPlugin(preloader);
53
- e.addShape(new ImageDrawer(e));
63
+ e.addPlugin(new ImagePreloaderPlugin(e));
64
+ e.addShape(shapeTypes, async () => {
65
+ const { ImageDrawer } = await import("./ImageDrawer.js");
66
+ return new ImageDrawer(e);
67
+ });
54
68
  });
55
69
  }
@@ -1,4 +1,6 @@
1
1
  export class ByteStream {
2
+ data;
3
+ pos;
2
4
  constructor(bytes) {
3
5
  this.pos = 0;
4
6
  this.data = new Uint8ClampedArray(bytes);
@@ -17,7 +19,7 @@ export class ByteStream {
17
19
  return this.data[this.pos - increment] + (this.data[this.pos - previous] << shift);
18
20
  }
19
21
  readSubBlocks() {
20
- let blockString = "", size = 0;
22
+ let blockString = "", size;
21
23
  const minCount = 0, emptySize = 0;
22
24
  do {
23
25
  size = this.data[this.pos++];